// 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 or the MIT license // , 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(uintptr handle); 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 win.HWnd = getHwnd(pid, isPid); #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) { XCloseDisplay(rDisplay); 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; XCloseDisplay(rDisplay); 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 }