#![allow(unused_doc_comments, unused_imports)]
#![doc = include_str!("../README.md")]
#[cfg(all(feature = "lite", feature = "opencl"))]
compile_error!("Features `lite` and `opencl` cannot be enabled at the same time.");
#[cfg(not(any(test, feature = "dev")))]
mod core;
#[cfg(not(any(test, feature = "dev")))]
mod data;
#[cfg(any(test, feature = "dev"))]
pub mod core;
#[cfg(any(test, feature = "dev"))]
pub mod data;
pub mod errors;
pub mod imgtools;
mod rustautogui_impl;
#[cfg(not(feature = "lite"))]
use data::*;
use crate::errors::*;
use std::{collections::HashMap, env};
#[cfg(not(feature = "lite"))]
use core::template_match;
use core::{
keyboard::Keyboard,
mouse::{mouse_position, Mouse, MouseScroll},
screen::Screen,
};
#[cfg(feature = "opencl")]
use crate::data::{DevicesInfo, OpenClData};
#[cfg(feature = "opencl")]
use ocl::{enums, Buffer, Context, Kernel, Program, Queue};
pub use core::mouse::mouse_position::print_mouse_position;
pub use core::mouse::MouseClick;
#[cfg(not(feature = "lite"))]
const DEFAULT_ALIAS: &str = "default_rsgui_!#123#!";
#[cfg(not(feature = "lite"))]
const DEFAULT_BCKP_ALIAS: &str = "bckp_tmpl_.#!123!#.";
#[derive(PartialEq, Debug)]
#[cfg(not(feature = "lite"))]
pub enum MatchMode {
Segmented,
FFT,
#[cfg(feature = "opencl")]
SegmentedOcl,
#[cfg(feature = "opencl")]
SegmentedOclV2,
}
#[cfg(not(feature = "lite"))]
impl Clone for MatchMode {
fn clone(&self) -> Self {
match self {
MatchMode::Segmented => MatchMode::Segmented,
MatchMode::FFT => MatchMode::FFT,
#[cfg(feature = "opencl")]
MatchMode::SegmentedOcl => MatchMode::SegmentedOcl,
#[cfg(feature = "opencl")]
MatchMode::SegmentedOclV2 => MatchMode::SegmentedOclV2,
}
}
}
#[allow(dead_code)]
pub struct RustAutoGui {
#[cfg(not(feature = "lite"))]
template_data: TemplateMatchingData,
debug: bool,
template_height: u32,
template_width: u32,
keyboard: Keyboard,
mouse: Mouse,
screen: Screen,
suppress_warnings: bool,
#[cfg(feature = "opencl")]
opencl_data: OpenClData,
}
impl RustAutoGui {
pub fn new(debug: bool) -> Result<Self, AutoGuiError> {
#[cfg(any(target_os = "windows", target_os = "macos"))]
let screen = Screen::new()?;
#[cfg(any(target_os = "windows", target_os = "macos"))]
let keyboard = Keyboard::new();
#[cfg(any(target_os = "windows", target_os = "macos"))]
let mouse_struct: Mouse = Mouse::new();
#[cfg(target_os = "linux")]
let screen = Screen::new();
#[cfg(target_os = "linux")]
let keyboard = Keyboard::new(screen.display);
#[cfg(target_os = "linux")]
let mouse_struct: Mouse = Mouse::new(screen.display, screen.root_window);
let suppress_warnings = env::var("RUSTAUTOGUI_SUPPRESS_WARNINGS")
.map(|val| val == "1" || val.eq_ignore_ascii_case("true"))
.unwrap_or(false);
#[cfg(feature = "opencl")]
let opencl_data = Self::setup_opencl(None)?;
#[cfg(not(feature = "lite"))]
let template_match_data = TemplateMatchingData {
template: None,
prepared_data: PreparedData::None,
prepared_data_stored: HashMap::new(),
match_mode: None,
region: (0, 0, 0, 0),
alias_used: DEFAULT_ALIAS.to_string(),
};
Ok(Self {
#[cfg(not(feature = "lite"))]
template_data: template_match_data,
debug,
template_width: 0,
template_height: 0,
keyboard,
mouse: mouse_struct,
screen,
suppress_warnings,
#[cfg(feature = "opencl")]
opencl_data: opencl_data,
})
}
#[cfg(feature = "dev")]
pub fn dev_setup_opencl(device_id: Option<u32>) -> Result<OpenClData, AutoGuiError> {
Self::setup_opencl(device_id)
}
#[cfg(feature = "opencl")]
fn setup_opencl(device_id: Option<u32>) -> Result<OpenClData, AutoGuiError> {
let context = Context::builder().build()?;
let available_devices = context.devices();
let device_count = available_devices.len();
let mut device_list: Vec<DevicesInfo> = Vec::new();
let mut highest_score = 0;
let mut best_device_index = 0;
let mut max_workgroup_size = 0;
for (i, device) in available_devices.into_iter().enumerate() {
let device_type = device.info(enums::DeviceInfo::Type)?.to_string();
let workgroup_size: u32 = device
.info(enums::DeviceInfo::MaxWorkGroupSize)?
.to_string()
.parse()
.map_err(|_| AutoGuiError::OSFailure("Failed to read GPU data".to_string()))?;
let global_mem: u64 = device
.info(enums::DeviceInfo::GlobalMemSize)?
.to_string()
.parse()
.map_err(|_| AutoGuiError::OSFailure("Failed to read GPU data".to_string()))?;
let compute_units: u32 = device
.info(enums::DeviceInfo::MaxComputeUnits)?
.to_string()
.parse()
.map_err(|_| AutoGuiError::OSFailure("Failed to read GPU data".to_string()))?;
let clock_frequency = device
.info(enums::DeviceInfo::MaxClockFrequency)?
.to_string()
.parse()
.map_err(|_| AutoGuiError::OSFailure("Failed to read GPU data".to_string()))?;
let device_vendor = device.info(enums::DeviceInfo::Vendor)?.to_string();
let device_name = device.info(enums::DeviceInfo::Name)?.to_string();
let global_mem_gb = global_mem / 1_048_576;
let score = global_mem_gb as u32 * 2 + compute_units * 10 + clock_frequency;
let gui_device = DevicesInfo::new(
device,
i as u32,
global_mem_gb as u32,
clock_frequency,
compute_units,
device_vendor,
device_name,
score,
);
device_list.push(gui_device);
match device_id {
Some(x) => {
if x as usize > device_count {
return Err(ocl::Error::from("No device found for the given index"))?;
}
if i == x as usize {
highest_score = score;
best_device_index = i;
max_workgroup_size = workgroup_size;
}
}
None => {
if score >= highest_score && device_type.contains("GPU") {
highest_score = score;
best_device_index = i;
max_workgroup_size = workgroup_size;
}
}
}
}
let used_device = context.devices()[best_device_index as usize];
let queue = Queue::new(&context, used_device, None)?;
let program_source = template_match::opencl_kernel::OCL_KERNEL;
let program = Program::builder().src(program_source).build(&context)?;
let opencl_data = OpenClData {
device_list: device_list,
ocl_program: program,
ocl_context: context,
ocl_queue: queue,
ocl_buffer_storage: HashMap::new(),
ocl_kernel_storage: HashMap::new(),
ocl_workgroup_size: max_workgroup_size,
};
Ok(opencl_data)
}
pub fn set_suppress_warnings(&mut self, suppress: bool) {
self.suppress_warnings = suppress;
}
pub fn change_debug_state(&mut self, state: bool) {
self.debug = state;
}
pub fn get_screen_size(&mut self) -> (i32, i32) {
self.screen.dimension()
}
#[cfg(not(feature = "lite"))]
pub fn save_screenshot(&mut self, path: &str) -> Result<(), AutoGuiError> {
self.screen.grab_screenshot(path)?;
Ok(())
}
#[cfg(feature = "opencl")]
pub fn list_devices(&self) {
for (i, item) in (&self.opencl_data.device_list).iter().enumerate() {
println!("Device {i}:");
println!("{}", item.print_device());
println!("\n");
}
}
#[cfg(feature = "opencl")]
pub fn change_ocl_device(&mut self, device_index: u32) -> Result<(), AutoGuiError> {
let new_opencl_data = Self::setup_opencl(Some(device_index))?;
self.opencl_data = new_opencl_data;
self.template_data.template = None;
self.template_data.prepared_data = PreparedData::None;
self.template_data.prepared_data_stored = HashMap::new();
self.template_width = 0;
self.template_height = 0;
self.template_data.alias_used = DEFAULT_ALIAS.to_string();
self.template_data.region = (0, 0, 0, 0);
self.template_data.match_mode = None;
Ok(())
}
#[cfg(not(feature = "lite"))]
fn check_if_region_out_of_bound(
&mut self,
template_width: u32,
template_height: u32,
region_x: u32,
region_y: u32,
region_width: u32,
region_height: u32,
) -> Result<(), AutoGuiError> {
if (region_x + region_width > self.screen.screen_width as u32)
| (region_y + region_height > self.screen.screen_height as u32)
{
return Err(AutoGuiError::OutOfBoundsError(
"Region size larger than screen size".to_string(),
));
}
if (template_width > (self.screen.screen_width as u32))
| (template_height > (self.screen.screen_height as u32))
{
return Err(AutoGuiError::OutOfBoundsError(
"Template size larger than screen size".to_string(),
));
}
#[cfg(not(feature = "lite"))]
if (template_width > region_width) | (template_height > region_height) {
return Err(AutoGuiError::OutOfBoundsError(
"Template size larger than region size".to_string(),
));
}
#[cfg(not(feature = "lite"))]
if template_height * template_width == 0 {
Err(ImageProcessingError::Custom(
"Template size = 0. Please check loaded template if its correct".to_string(),
))?;
}
Ok(())
}
}
#[cfg(target_os = "linux")]
impl Drop for RustAutoGui {
fn drop(&mut self) {
self.screen.destroy();
}
}
#[cfg(not(feature = "lite"))]
#[cfg(target_os = "windows")]
impl Drop for RustAutoGui {
fn drop(&mut self) {
self.screen.destroy();
}
}