mirror of
https://github.com/go-vgo/robotgo.git
synced 2025-05-29 21:43:55 +00:00
613 lines
14 KiB
C
613 lines
14 KiB
C
// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
#include "pub.h"
|
|
|
|
bool setHandle(uintptr handle);
|
|
bool is_valid();
|
|
bool IsAxEnabled(bool options);
|
|
|
|
MData get_active(void);
|
|
void initWindow();
|
|
char* get_title_by_hand(MData m_data);
|
|
void close_window_by_Id(MData m_data);
|
|
|
|
// int findwindow()
|
|
uintptr initHandle = 0;
|
|
|
|
void initWindow(uintptr handle){
|
|
#if defined(IS_MACOSX)
|
|
mData.CgID = 0;
|
|
mData.AxID = 0;
|
|
#elif defined(USE_X11)
|
|
Display *rDisplay = XOpenDisplay(NULL);
|
|
// If atoms loaded
|
|
if (WM_PID == None) {
|
|
// Load all necessary atom properties
|
|
if (rDisplay != NULL) {LoadAtoms();}
|
|
}
|
|
|
|
mData.XWin = 0;
|
|
XCloseDisplay(rDisplay);
|
|
#elif defined(IS_WINDOWS)
|
|
mData.HWnd = 0;
|
|
#endif
|
|
setHandle(handle);
|
|
}
|
|
|
|
bool Is64Bit() {
|
|
#ifdef RobotGo_64
|
|
return true;
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
MData set_handle_pid(uintptr pid, int8_t isPid){
|
|
MData win;
|
|
#if defined(IS_MACOSX)
|
|
// Handle to a AXUIElementRef
|
|
win.AxID = AXUIElementCreateApplication(pid);
|
|
#elif defined(USE_X11)
|
|
win.XWin = (Window)pid; // Handle to an X11 window
|
|
#elif defined(IS_WINDOWS)
|
|
// win.HWnd = (HWND)pid; // Handle to a window HWND
|
|
if (isPid == 0) {
|
|
win.HWnd = GetHwndByPid(pid);
|
|
} else {
|
|
win.HWnd = (HWND)pid;
|
|
}
|
|
#endif
|
|
|
|
return win;
|
|
}
|
|
|
|
void set_handle_pid_mData(uintptr pid, int8_t isPid){
|
|
MData win = set_handle_pid(pid, isPid);
|
|
mData = win;
|
|
}
|
|
|
|
bool is_valid() {
|
|
initWindow(initHandle);
|
|
if (!IsAxEnabled(true)) {
|
|
printf("%s\n", "Window: Accessibility API is disabled!\n"
|
|
"Failed to enable access for assistive devices.");
|
|
}
|
|
MData actdata = get_active();
|
|
|
|
#if defined(IS_MACOSX)
|
|
mData.CgID = actdata.CgID;
|
|
mData.AxID = actdata.AxID;
|
|
if (mData.CgID == 0 || mData.AxID == 0) { return false; }
|
|
|
|
CFTypeRef r = NULL;
|
|
// Attempt to get the window role
|
|
if (AXUIElementCopyAttributeValue(mData.AxID, kAXRoleAttribute, &r) == kAXErrorSuccess && r){
|
|
CFRelease(r);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
#elif defined(USE_X11)
|
|
mData.XWin = actdata.XWin;
|
|
if (mData.XWin == 0) { return false; }
|
|
|
|
Display *rDisplay = XOpenDisplay(NULL);
|
|
// Check for a valid X-Window display
|
|
if (rDisplay == NULL) { return false; }
|
|
|
|
// Ignore X errors
|
|
XDismissErrors();
|
|
|
|
// Get the window PID property
|
|
void* result = GetWindowProperty(mData, WM_PID,NULL);
|
|
if (result == NULL) { return false; }
|
|
|
|
// Free result and return true
|
|
XFree(result);
|
|
XCloseDisplay(rDisplay);
|
|
|
|
return true;
|
|
#elif defined(IS_WINDOWS)
|
|
mData.HWnd = actdata.HWnd;
|
|
if (mData.HWnd == 0) {
|
|
return false;
|
|
}
|
|
|
|
return IsWindow(mData.HWnd) != 0;
|
|
#endif
|
|
}
|
|
|
|
bool IsAxEnabled(bool options){
|
|
#if defined(IS_MACOSX)
|
|
// Statically load all required functions one time
|
|
static dispatch_once_t once; dispatch_once (&once,
|
|
^{
|
|
// Open the framework
|
|
void* handle = dlopen("/System/Library/Frameworks/Application"
|
|
"Services.framework/ApplicationServices", RTLD_LAZY);
|
|
|
|
// Validate the handle
|
|
if (handle != NULL) {
|
|
*(void**) (&gAXIsProcessTrustedWithOptions) = dlsym (handle, "AXIsProcessTrustedWithOptions");
|
|
gkAXTrustedCheckOptionPrompt = (CFStringRef*) dlsym (handle, "kAXTrustedCheckOptionPrompt");
|
|
}
|
|
});
|
|
|
|
// Check for new OSX 10.9 function
|
|
if (gAXIsProcessTrustedWithOptions) {
|
|
// Check whether to show prompt
|
|
CFBooleanRef displayPrompt = options ? kCFBooleanTrue : kCFBooleanFalse;
|
|
|
|
// Convert display prompt value into a dictionary
|
|
const void* k[] = { *gkAXTrustedCheckOptionPrompt };
|
|
const void* v[] = { displayPrompt };
|
|
CFDictionaryRef o = CFDictionaryCreate(NULL, k, v, 1, NULL, NULL);
|
|
|
|
// Determine whether the process is actually trusted
|
|
bool result = (*gAXIsProcessTrustedWithOptions)(o);
|
|
// Free memory
|
|
CFRelease(o);
|
|
return result;
|
|
} else {
|
|
// Ignore deprecated warnings
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
// Check whether we have accessibility access
|
|
return AXAPIEnabled() || AXIsProcessTrusted();
|
|
#pragma clang diagnostic pop
|
|
}
|
|
#elif defined(USE_X11)
|
|
return true;
|
|
#elif defined(IS_WINDOWS)
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
// int
|
|
bool setHandle(uintptr handle){
|
|
#if defined(IS_MACOSX)
|
|
// Release the AX element
|
|
if (mData.AxID != NULL) {
|
|
CFRelease(mData.AxID);
|
|
}
|
|
|
|
// Reset both values
|
|
mData.CgID = 0;
|
|
mData.AxID = 0;
|
|
|
|
if (handle == 0) {
|
|
// return 0;
|
|
return true;
|
|
}
|
|
|
|
// Retrieve the window element
|
|
CGWindowID cgID = (CGWindowID)handle;
|
|
AXUIElementRef axID = GetUIElement(cgID);
|
|
if (axID != NULL){
|
|
mData.CgID = cgID;
|
|
mData.AxID = axID;
|
|
// return 0;
|
|
return true;
|
|
}
|
|
|
|
// return 1;
|
|
return false;
|
|
#elif defined(USE_X11)
|
|
mData.XWin = (Window)handle;
|
|
if (handle == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (is_valid()) {
|
|
return true;
|
|
}
|
|
|
|
mData.XWin = 0;
|
|
return false;
|
|
#elif defined(IS_WINDOWS)
|
|
mData.HWnd = (HWND)handle;
|
|
if (handle == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (is_valid()) {
|
|
return true;
|
|
}
|
|
|
|
mData.HWnd = 0;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool IsTopMost(void){
|
|
// Check the window validity
|
|
if (!is_valid()) { return false; }
|
|
#if defined(IS_MACOSX)
|
|
return false; // WARNING: Unavailable
|
|
#elif defined(USE_X11)
|
|
// Ignore X errors
|
|
// XDismissErrors ();
|
|
// return GetState (mData.XWin, STATE_TOPMOST);
|
|
#elif defined(IS_WINDOWS)
|
|
return (GetWindowLongPtr(mData.HWnd, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
|
|
#endif
|
|
}
|
|
|
|
bool IsMinimized(void){
|
|
// Check the window validity
|
|
if (!is_valid()) { return false; }
|
|
#if defined(IS_MACOSX)
|
|
CFBooleanRef data = NULL;
|
|
// Determine whether the window is minimized
|
|
if (AXUIElementCopyAttributeValue(mData.AxID, kAXMinimizedAttribute,
|
|
(CFTypeRef*) &data) == kAXErrorSuccess && data != NULL) {
|
|
// Convert resulting data into a bool
|
|
bool result = CFBooleanGetValue(data);
|
|
CFRelease(data);
|
|
return result;
|
|
}
|
|
|
|
return false;
|
|
#elif defined(USE_X11)
|
|
// Ignore X errors
|
|
// XDismissErrors();
|
|
// return GetState(mData.XWin, STATE_MINIMIZE);
|
|
#elif defined(IS_WINDOWS)
|
|
return (GetWindowLongPtr(mData.HWnd, GWL_STYLE) & WS_MINIMIZE) != 0;
|
|
#endif
|
|
}
|
|
|
|
//////
|
|
bool IsMaximized(void){
|
|
// Check the window validity
|
|
if (!is_valid()) { return false; }
|
|
#if defined(IS_MACOSX)
|
|
return false; // WARNING: Unavailable
|
|
#elif defined(USE_X11)
|
|
// Ignore X errors
|
|
// XDismissErrors();
|
|
// return GetState(mData.XWin, STATE_MAXIMIZE);
|
|
#elif defined(IS_WINDOWS)
|
|
return (GetWindowLongPtr(mData.HWnd, GWL_STYLE) & WS_MAXIMIZE) != 0;
|
|
#endif
|
|
}
|
|
|
|
void set_active(const MData win) {
|
|
// Check if the window is valid
|
|
if (!is_valid()) { return; }
|
|
#if defined(IS_MACOSX)
|
|
// Attempt to raise the specified window object
|
|
if (AXUIElementPerformAction(win.AxID, kAXRaiseAction) != kAXErrorSuccess) {
|
|
pid_t pid = 0;
|
|
// Attempt to retrieve the PID of the window
|
|
if (AXUIElementGetPid(win.AxID, &pid) != kAXErrorSuccess || !pid) { return; }
|
|
|
|
// Ignore deprecated warnings
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
ProcessSerialNumber psn;
|
|
// Attempt to retrieve the process psn
|
|
if (GetProcessForPID(pid, &psn) == 0) {
|
|
// Gracefully activate process
|
|
SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly);
|
|
}
|
|
|
|
#pragma clang diagnostic pop
|
|
}
|
|
#elif defined(USE_X11)
|
|
// Ignore X errors
|
|
XDismissErrors();
|
|
|
|
// Go to the specified window's desktop
|
|
SetDesktopForWindow(win);
|
|
Display *rDisplay = XOpenDisplay(NULL);
|
|
// Check the atom value
|
|
if (WM_ACTIVE != None) {
|
|
// Retrieve the screen number
|
|
XWindowAttributes attr = { 0 };
|
|
XGetWindowAttributes(rDisplay, win.XWin, &attr);
|
|
int s = XScreenNumberOfScreen(attr.screen);
|
|
|
|
// Prepare an event
|
|
XClientMessageEvent e = { 0 };
|
|
e.window = win.XWin;
|
|
e.format = 32;
|
|
e.message_type = WM_ACTIVE;
|
|
e.display = rDisplay;
|
|
e.type = ClientMessage;
|
|
e.data.l[0] = 2;
|
|
e.data.l[1] = CurrentTime;
|
|
|
|
// Send the message
|
|
XSendEvent(rDisplay, XRootWindow(rDisplay, s), False,
|
|
SubstructureNotifyMask | SubstructureRedirectMask,
|
|
(XEvent*) &e);
|
|
} else {
|
|
// Attempt to raise the specified window
|
|
XRaiseWindow(rDisplay, win.XWin);
|
|
// Set the specified window's input focus
|
|
XSetInputFocus(rDisplay, win.XWin, RevertToParent, CurrentTime);
|
|
}
|
|
XCloseDisplay(rDisplay);
|
|
#elif defined(IS_WINDOWS)
|
|
if (IsMinimized()) {
|
|
ShowWindow(win.HWnd, SW_RESTORE);
|
|
}
|
|
|
|
SetForegroundWindow(win.HWnd);
|
|
#endif
|
|
}
|
|
|
|
MData get_active(void) {
|
|
#if defined(IS_MACOSX)
|
|
MData result;
|
|
// Ignore deprecated warnings
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
ProcessSerialNumber psn; pid_t pid;
|
|
// Attempt to retrieve the front process
|
|
if (GetFrontProcess(&psn) != 0 || GetProcessPID(&psn, &pid) != 0) {
|
|
return result;
|
|
}
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
// Create accessibility object using focused PID
|
|
AXUIElementRef focused = AXUIElementCreateApplication(pid);
|
|
if (focused == NULL) { return result; } // Verify
|
|
|
|
AXUIElementRef element;
|
|
// Retrieve the currently focused window
|
|
if (AXUIElementCopyAttributeValue(focused, kAXFocusedWindowAttribute, (CFTypeRef*) &element)
|
|
== kAXErrorSuccess && element) {
|
|
|
|
CGWindowID win = 0;
|
|
// Use undocumented API to get WID
|
|
if (_AXUIElementGetWindow(element, &win) == kAXErrorSuccess && win) {
|
|
// Manually set internals
|
|
result.CgID = win;
|
|
result.AxID = element;
|
|
} else {
|
|
CFRelease(element);
|
|
}
|
|
}
|
|
CFRelease(focused);
|
|
|
|
return result;
|
|
#elif defined(USE_X11)
|
|
MData result;
|
|
Display *rDisplay = XOpenDisplay(NULL);
|
|
// Check X-Window display
|
|
if (WM_ACTIVE == None || rDisplay == NULL) {
|
|
return result;
|
|
}
|
|
|
|
// Ignore X errors
|
|
XDismissErrors();
|
|
|
|
// Get the current active window
|
|
result.XWin = XDefaultRootWindow(rDisplay);
|
|
void* active = GetWindowProperty(result, WM_ACTIVE, NULL);
|
|
|
|
// Check result value
|
|
if (active != NULL) {
|
|
// Extract window from the result
|
|
long window = *((long*)active);
|
|
XFree(active);
|
|
|
|
if (window != 0) {
|
|
// Set and return the foreground window
|
|
result.XWin = (Window)window;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Use input focus instead
|
|
Window window = None;
|
|
int revert = RevertToNone;
|
|
XGetInputFocus(rDisplay, &window, &revert);
|
|
XCloseDisplay(rDisplay);
|
|
|
|
// Return foreground window
|
|
result.XWin = window;
|
|
return result;
|
|
#elif defined(IS_WINDOWS)
|
|
// Attempt to get the foreground window multiple times in case
|
|
MData result;
|
|
|
|
uint8_t times = 0;
|
|
while (++times < 20) {
|
|
HWND handle;
|
|
handle = GetForegroundWindow();
|
|
if (handle != NULL) {
|
|
// mData.HWnd = (uintptr) handle;
|
|
result.HWnd = (HWND)handle;
|
|
return result;
|
|
}
|
|
Sleep(20);
|
|
}
|
|
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
void SetTopMost(bool state){
|
|
// Check window validity
|
|
if (!is_valid()) { return; }
|
|
#if defined(IS_MACOSX)
|
|
// WARNING: Unavailable
|
|
#elif defined(USE_X11)
|
|
// Ignore X errors
|
|
// XDismissErrors();
|
|
// SetState(mData.XWin, STATE_TOPMOST, state);
|
|
#elif defined(IS_WINDOWS)
|
|
SetWindowPos(mData.HWnd, state ? HWND_TOPMOST : HWND_NOTOPMOST,
|
|
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
|
|
#endif
|
|
}
|
|
|
|
void close_main_window () {
|
|
// Check if the window is valid
|
|
if (!is_valid()) { return; }
|
|
|
|
close_window_by_Id(mData);
|
|
}
|
|
|
|
void close_window_by_PId(uintptr pid, int8_t isPid){
|
|
MData win = set_handle_pid(pid, isPid);
|
|
close_window_by_Id(win);
|
|
}
|
|
|
|
// CloseWindow
|
|
void close_window_by_Id(MData m_data){
|
|
// Check window validity
|
|
if (!is_valid()) { return; }
|
|
#if defined(IS_MACOSX)
|
|
AXUIElementRef b = NULL;
|
|
// Retrieve the close button of this window
|
|
if (AXUIElementCopyAttributeValue(m_data.AxID, kAXCloseButtonAttribute, (CFTypeRef*) &b)
|
|
== kAXErrorSuccess && b != NULL) {
|
|
// Simulate button press on the close button
|
|
AXUIElementPerformAction(b, kAXPressAction);
|
|
CFRelease(b);
|
|
}
|
|
#elif defined(USE_X11)
|
|
Display *rDisplay = XOpenDisplay(NULL);
|
|
// Ignore X errors
|
|
XDismissErrors();
|
|
|
|
// Close the window
|
|
XDestroyWindow(rDisplay, m_data.XWin);
|
|
XCloseDisplay(rDisplay);
|
|
#elif defined(IS_WINDOWS)
|
|
PostMessage(m_data.HWnd, WM_CLOSE, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
char* get_main_title(){
|
|
// Check if the window is valid
|
|
if (!is_valid()) { return "is_valid failed."; }
|
|
|
|
return get_title_by_hand(mData);
|
|
}
|
|
|
|
char* get_title_by_pid(uintptr pid, int8_t isPid){
|
|
MData win = set_handle_pid(pid, isPid);
|
|
return get_title_by_hand(win);
|
|
}
|
|
|
|
char* named(void *result) {
|
|
char *name = (char*)calloc(strlen(result)+1, sizeof(char*));
|
|
char *rptr = (char*)result;
|
|
char *nptr = name;
|
|
while (*rptr) {
|
|
*nptr = *rptr;
|
|
nptr++;
|
|
rptr++;
|
|
}
|
|
*nptr = '\0';
|
|
|
|
return name;
|
|
}
|
|
|
|
char* get_title_by_hand(MData m_data){
|
|
// Check if the window is valid
|
|
if (!is_valid()) { return "is_valid failed."; }
|
|
#if defined(IS_MACOSX)
|
|
CFStringRef data = NULL;
|
|
// Determine the current title of the window
|
|
if (AXUIElementCopyAttributeValue(m_data.AxID, kAXTitleAttribute, (CFTypeRef*) &data) ==
|
|
kAXErrorSuccess && data != NULL) {
|
|
char conv[512];
|
|
// Convert result to a C-String
|
|
CFStringGetCString(data, conv, 512, kCFStringEncodingUTF8);
|
|
CFRelease(data);
|
|
|
|
char* s = (char*)calloc(100, sizeof(char*));
|
|
if (s) { strcpy(s, conv); }
|
|
// return (char *)&conv;
|
|
return s;
|
|
}
|
|
|
|
return "";
|
|
#elif defined(USE_X11)
|
|
void* result;
|
|
// Ignore X errors
|
|
XDismissErrors();
|
|
|
|
// Get window title (UTF-8)
|
|
result = GetWindowProperty(m_data, WM_NAME, NULL);
|
|
// Check result value
|
|
if (result != NULL) {
|
|
// Convert result to a string
|
|
char* name = named(result);
|
|
XFree(result);
|
|
|
|
if (name != NULL) { return name; }
|
|
}
|
|
|
|
// Get window title (ASCII)
|
|
result = GetWindowProperty(m_data, XA_WM_NAME, NULL);
|
|
// Check result value
|
|
if (result != NULL) {
|
|
// Convert result to a string
|
|
char* name = named(result);
|
|
XFree(result);
|
|
|
|
return name;
|
|
}
|
|
|
|
return "";
|
|
#elif defined(IS_WINDOWS)
|
|
if (GetWindowText(m_data.HWnd, m_data.Title, 512) > 0){
|
|
char* name = m_data.Title;
|
|
|
|
char* str = (char*)calloc(100, sizeof(char*));
|
|
if (str) { strcpy(str, name); }
|
|
return str;
|
|
}
|
|
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
int32_t get_PID(void) {
|
|
// Check window validity
|
|
if (!is_valid()) { return 0; }
|
|
#if defined(IS_MACOSX)
|
|
pid_t pid = 0;
|
|
// Attempt to retrieve the window pid
|
|
if (AXUIElementGetPid(mData.AxID, &pid)== kAXErrorSuccess) {
|
|
return pid;
|
|
}
|
|
return 0;
|
|
#elif defined(USE_X11)
|
|
// Ignore X errors
|
|
XDismissErrors();
|
|
|
|
// Get the window PID
|
|
long* result = (long*)GetWindowProperty(mData, WM_PID,NULL);
|
|
// Check result and convert it
|
|
if (result == NULL) { return 0; }
|
|
|
|
int32_t pid = (int32_t) *result;
|
|
XFree(result);
|
|
return pid;
|
|
#elif defined(IS_WINDOWS)
|
|
DWORD id = 0;
|
|
GetWindowThreadProcessId(mData.HWnd, &id);
|
|
return id;
|
|
#endif
|
|
}
|