#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
use libc::{c_int, c_ulong, c_ushort, c_void, wchar_t};
use std::{ffi::c_char, ptr, slice};
use crate::{
common::{base::errors::PrintersError, traits::platform::PlatformPrinterJobGetters},
windows::utils::{
date::{calculate_system_time, get_current_epoch},
memory::alloc_s,
strings::{str_to_wide_string, wchar_t_to_string},
},
};
#[link(name = "winspool")]
unsafe extern "system" {
fn OpenPrinterW(
pPrinterName: *const wchar_t,
phPrinter: *mut *mut c_void,
pDefault: *mut PrinterDefaultW,
) -> c_int;
fn StartDocPrinterW(
hPrinter: *mut c_void,
Level: c_ulong,
pDocInfo: *const DocInfo1,
) -> c_ulong;
fn StartPagePrinter(hPrinter: *mut c_void) -> c_int;
fn WritePrinter(
hPrinter: *mut c_void,
pBuf: *mut c_void,
cbBuf: c_ulong,
pcWritten: *mut c_ulong,
) -> c_int;
fn EndPagePrinter(hPrinter: *mut c_void) -> c_int;
fn EndDocPrinter(hPrinter: *mut c_void) -> c_int;
fn ClosePrinter(hPrinter: *mut c_void) -> c_int;
fn EnumJobsW(
hPrinter: *mut c_void,
firstJob: c_ulong,
noJobs: c_ulong,
level: c_ulong,
pJob: *mut c_void,
cbBuf: c_ulong,
pcbNeeded: *mut c_ulong,
pcReturned: *mut c_ulong,
) -> c_int;
fn SetJobW(
hPrinter: *mut c_void,
JobId: c_ulong,
Level: c_ulong,
pJob: *mut c_char,
Command: c_ulong,
) -> c_int;
}
#[repr(C)]
struct PrinterDefaultW {
pDatatype: *mut wchar_t,
pDevMode: *mut c_void,
DesiredAccess: c_ulong,
}
#[repr(C)]
struct DocInfo1 {
pDocName: *mut wchar_t,
pOutputFile: *mut wchar_t,
pDatatype: *mut wchar_t,
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct SYSTEMTIME {
pub wYear: c_ushort,
pub wMonth: c_ushort,
pub wDayOfWeek: c_ushort,
pub wDay: c_ushort,
pub wHour: c_ushort,
pub wMinute: c_ushort,
pub wSecond: c_ushort,
pub wMilliseconds: c_ushort,
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct JOB_INFO_1W {
JobId: c_ulong,
pPrinterName: *mut wchar_t,
pMachineName: *mut wchar_t,
pUserName: *mut wchar_t,
pDocument: *mut wchar_t,
pDatatype: *mut wchar_t,
pStatus: *mut wchar_t,
Status: c_ulong,
Priority: c_ulong,
Position: c_ulong,
TotalPages: c_ulong,
PagesPrinted: c_ulong,
Submitted: SYSTEMTIME,
}
impl PlatformPrinterJobGetters for JOB_INFO_1W {
fn get_id(&self) -> u64 {
self.JobId.into()
}
fn get_name(&self) -> String {
wchar_t_to_string(self.pDocument)
}
fn get_state(&self) -> u64 {
self.Status.into()
}
fn get_printer(&self) -> String {
wchar_t_to_string(self.pPrinterName)
}
fn get_media_type(&self) -> String {
wchar_t_to_string(self.pDatatype)
}
fn get_created_at(&self) -> std::time::SystemTime {
calculate_system_time(
self.Submitted.wYear,
self.Submitted.wMonth,
self.Submitted.wDay,
self.Submitted.wHour,
self.Submitted.wMinute,
self.Submitted.wSecond,
self.Submitted.wMilliseconds,
)
}
fn get_processed_at(&self) -> Option<std::time::SystemTime> {
Some(self.get_created_at())
}
fn get_completed_at(&self) -> Option<std::time::SystemTime> {
Some(self.get_created_at())
}
}
fn open_printer(printer_name: &str) -> Result<*mut c_void, PrintersError> {
let printer_name = str_to_wide_string(printer_name);
let mut printer_handle: *mut c_void = ptr::null_mut();
return if unsafe {
OpenPrinterW(
printer_name.as_ptr() as *const wchar_t,
&mut printer_handle,
ptr::null_mut(),
)
} == 0
{
Err(PrintersError::job_error("OpenPrinterW failed"))
} else {
Ok(printer_handle)
};
}
pub fn print_buffer(
printer_name: &str,
job_name: Option<&str>,
buffer: &[u8],
options: &[(&str, &str)],
) -> Result<u64, PrintersError> {
unsafe {
let printer_handle = open_printer(printer_name)?;
let mut copies = 1;
let mut data_type = "RAW";
for option in options {
match option.0 {
"copies" => copies = option.1.parse().unwrap_or(copies),
"document-format" => data_type = option.1,
_ => {}
}
}
let mut p_data_type = str_to_wide_string(data_type);
let mut p_doc_name =
str_to_wide_string(job_name.unwrap_or(get_current_epoch().to_string().as_str()));
let doc_info = DocInfo1 {
pDocName: p_doc_name.as_mut_ptr() as *mut wchar_t,
pDatatype: p_data_type.as_mut_ptr() as *mut wchar_t,
pOutputFile: ptr::null_mut(),
};
let job_id = StartDocPrinterW(printer_handle, 1, &doc_info);
if job_id == 0 {
ClosePrinter(printer_handle);
return Err(PrintersError::job_error("StartDocPrinterW failed"));
}
for _ in 0..copies {
if StartPagePrinter(printer_handle) != 0 {
let mut bytes_written: c_ulong = 0;
WritePrinter(
printer_handle,
buffer.as_ptr() as *mut c_void,
buffer.len() as c_ulong,
&mut bytes_written,
);
EndPagePrinter(printer_handle);
}
}
EndDocPrinter(printer_handle);
ClosePrinter(printer_handle);
Ok(job_id as u64)
}
}
pub fn enum_printer_jobs(printer_name: &str) -> Result<&'static [JOB_INFO_1W], PrintersError> {
let printer_handle = open_printer(printer_name)?;
let mut enum_result = 0;
let mut buffer_ptr: *mut JOB_INFO_1W = ptr::null_mut();
let mut jobs_count: c_ulong = 0;
let mut bytes_needed: c_ulong = 0;
for _ in 0..2 {
enum_result = unsafe {
EnumJobsW(
printer_handle,
0,
0xFFFFFFFF,
1,
buffer_ptr as *mut c_void,
bytes_needed,
&mut bytes_needed,
&mut jobs_count,
)
};
if enum_result != 0 || bytes_needed == 0 {
break;
}
buffer_ptr = alloc_s::<JOB_INFO_1W>(bytes_needed);
}
unsafe { ClosePrinter(printer_handle) };
if enum_result == 0 {
return Err(PrintersError::job_error("EnumJobsW failed"));
}
Ok(if jobs_count > 0 {
unsafe { slice::from_raw_parts(buffer_ptr as *const JOB_INFO_1W, jobs_count as usize) }
} else {
&[]
})
}
pub fn set_job_state(printer_name: &str, command: u64, job_id: u64) -> Result<(), PrintersError> {
unsafe {
let printer_handle = open_printer(printer_name)?;
let result = SetJobW(
printer_handle,
job_id as c_ulong,
0,
ptr::null_mut(),
command as c_ulong,
);
ClosePrinter(printer_handle);
if result == 0 {
Err(PrintersError::job_error("SetJobW failed"))
} else {
Ok(())
}
}
}