📄 wm_state.c
字号:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <X11/Xlib.h>#include <X11/Xutil.h>#include <X11/keysym.h>#include <X11/Xatom.h>#include <inttypes.h>#include <string.h>#include "debug_print.h"#include "display.h"#include "wm_state.h"static WindowState_t current_state = WINDOW_STATE_NORMAL;typedef struct { int x; int y; int width; int height;} geometry_t;static geometry_t normal_state_geometry;static unsigned long req_serial; /* used for error handling */static int (*prev_xerrhandler)(Display *dpy, XErrorEvent *ev);static void remove_motif_decorations(Display *dpy, Window win);static void disable_motif_decorations(Display *dpy, Window win);static int EWMH_wm;static int gnome_wm;static int gnome_wm_layers;static int has_ewmh_state_fullscreen;static char *wm_name = NULL;static Atom wm_state_atom;#define MWM_HINTS_DECORATIONS (1L << 1)typedef struct { uint32_t flags; uint32_t functions; uint32_t decorations; int32_t input_mode; uint32_t status;} mwmhints_t;static void remove_motif_decorations(Display *dpy, Window win){ Atom WM_HINTS; /* can only change decorations while unmapped ? */ /* First try to set MWM hints */ WM_HINTS = XInternAtom(dpy, "_MOTIF_WM_HINTS", True); if(WM_HINTS != None) { /* Hints used by Motif compliant window managers */ mwmhints_t MWMHints = { MWM_HINTS_DECORATIONS, 0, 0, 0, 0 }; XChangeProperty(dpy, win, WM_HINTS, WM_HINTS, 32, PropModeReplace, (unsigned char *)&MWMHints, sizeof(MWMHints)/sizeof(long)); } else { WARNING("%s", "_MOTIF_WM_HINTS atom not found\n"); }}static void disable_motif_decorations(Display *dpy, Window win){ Atom WM_HINTS; /* can only change decorations while unmapped ? */ /* First try to unset MWM hints */ WM_HINTS = XInternAtom(dpy, "_MOTIF_WM_HINTS", True); if(WM_HINTS != None) { /* Hints used by Motif compliant window managers */ XDeleteProperty(dpy, win, WM_HINTS); } else { WARNING("%s", "_MOTIF_WM_HINTS atom not found\n"); } }static void calc_coords(Display *dpy, Window win, int *x, int *y, XEvent *ev){ int dest_x_ret; int dest_y_ret; Window dest_win; DNOTE("configure x: %d, y: %d\n", ev->xconfigure.x, ev->xconfigure.y); if(ev->xconfigure.send_event == True) { //DNOTE("send_event: True\n"); *x = ev->xconfigure.x; *y = ev->xconfigure.y; XTranslateCoordinates(dpy, win, DefaultRootWindow(dpy), 0, 0, &dest_x_ret, &dest_y_ret, &dest_win); if(*x != dest_x_ret) { DNOTE("wm_x: %d, xtranslate_x: %d\n", *x, dest_x_ret); *x = dest_x_ret; } if(*y != dest_y_ret) { DNOTE("wm_y: %d, xtranslate_y: %d\n", *y, dest_y_ret); *y = dest_y_ret; } } else { //DNOTE("send_event: False\n"); XTranslateCoordinates(dpy, win, DefaultRootWindow(dpy), 0, 0, &dest_x_ret, &dest_y_ret, &dest_win); DNOTE("xtranslate x: %d, y: %d\n", dest_x_ret, dest_y_ret); *x = dest_x_ret; *y = dest_y_ret; } //DNOTE("x: %d, y: %d\n", *x, *y);}static void save_normal_geometry(Display *dpy, Window win){ // Ugly hack so we can reposition ourself when going from fullscreen Window root_return; int x,y; unsigned int bwidth, depth; int dest_x_ret, dest_y_ret; Window dest_win; XGetGeometry(dpy, win, &root_return, &x, &y, &normal_state_geometry.width, &normal_state_geometry.height, &bwidth, &depth); XTranslateCoordinates(dpy, win, DefaultRootWindow(dpy), 0, 0, &dest_x_ret, &dest_y_ret, &dest_win); normal_state_geometry.x = dest_x_ret; normal_state_geometry.y = dest_y_ret; DNOTE("x_ret: %d, y_ret: %d\n", dest_x_ret, dest_y_ret); DNOTE("normal_state_geometry: x: %d, y: %d, w: %d, h: %d, bw: %d, d: %d\n", x, y, normal_state_geometry.width, normal_state_geometry.height, bwidth, depth); }static void restore_normal_geometry(Display *dpy, Window win){ XWindowChanges win_changes; XEvent ev, ret_ev; int x, y; int n; // Try to resize win_changes.x = normal_state_geometry.x; win_changes.y = normal_state_geometry.y; win_changes.width = normal_state_geometry.width; win_changes.height = normal_state_geometry.height; //win_changes.stack_mode = Above; XReconfigureWMWindow(dpy, win, 0, CWX | CWY | CWWidth | CWHeight, &win_changes); // Wait for a configure notify do { XNextEvent(dpy, &ev); } while(ev.type != ConfigureNotify); // save the configure event so we can return it ret_ev = ev; calc_coords(dpy, win, &x, &y, &ev); for(n = 0; n < 10; n++) { while(XCheckTypedEvent(dpy, ConfigureNotify, &ev) == True) { ret_ev = ev; calc_coords(dpy, win, &x, &y, &ev); } if(abs(x-win_changes.x) < 100 && abs(y-win_changes.y) < 100) { break; } usleep(100000); } //DNOTE("no more configure notify\n"); //try to move one more time, maybe we didn't get all configure events? if(x != win_changes.x || y != win_changes.y) { DNOTE("window is not at %d, %d. Trying to move again\n", win_changes.x, win_changes.y); XSync(dpy, True); XReconfigureWMWindow(dpy, win, 0, CWX | CWY, &win_changes); /* do { XNextEvent(dpy, &ev); } while(ev.type != ConfigureNotify); ret_ev = ev; calc_coords(dpy, win, &x, &y, &ev); */ while(XCheckTypedEvent(dpy, ConfigureNotify, &ev) == True) { ret_ev = ev; calc_coords(dpy, win, &x, &y, &ev); } } // ugly hack, but what can you do // when we don't end up at (win_changes.x, win_changes.y) // try to compensate and move one more time if((x != win_changes.x || y != win_changes.y) && abs(x - win_changes.x) < 100 && abs(y - win_changes.y) < 100) { XWindowChanges win_compensate; win_compensate.x = win_changes.x+win_changes.x-x; win_compensate.y = win_changes.y+win_changes.y-y; DNOTE("window is not at %d, %d\n", win_changes.x, win_changes.y); DNOTE("Compensating by moving to x: %d= %d+%d-%d, y: %d= %d+%d-%d\n", win_compensate.x, win_changes.x, win_changes.x, x, win_compensate.y, win_changes.y, win_changes.y, y); XSync(dpy, True); XReconfigureWMWindow(dpy, win, 0, CWX | CWY, &win_compensate); do { XNextEvent(dpy, &ev); } while(ev.type != ConfigureNotify); ret_ev = ev; calc_coords(dpy, win, &x, &y, &ev); while(XCheckTypedEvent(dpy, ConfigureNotify, &ev) == True) { ret_ev = ev; calc_coords(dpy, win, &x, &y, &ev); } if(x != win_changes.x || y != win_changes.y) { DNOTE("Couldn't place window at %d,%d\n", win_changes.x, win_changes.y); DNOTE("Window is at %d, %d\n", x, y); } else { //DNOTE("Fixed, window is now at %d,%d\n", win_changes.x, win_changes.y); } } XPutBackEvent(dpy, &ev); }int get_wm_state(Display *dpy, Window win){ Atom actual_type_return; int actual_format_return; unsigned long nitems_return; unsigned long bytes_after_return; unsigned char* prop_return; int wm_state; XGetWindowProperty(dpy, win, wm_state_atom, 0, 1, False, wm_state_atom, &actual_type_return, &actual_format_return, &nitems_return, &bytes_after_return, &prop_return); if(actual_type_return == None) { return -1; //the property does not exist } else if(actual_type_return != wm_state_atom) { WARNING("WM_STATE is not of type WM_STATE: %lu\n", (unsigned long)actual_type_return); return -1; //wrong property type } else { wm_state = (int)(((unsigned long *)prop_return)[0]); XFree(prop_return); return wm_state; }} static Bool withdraw_predicate(Display *dpy, XEvent *ev, XPointer arg){ if(ev->type == UnmapNotify || ev->type == ReparentNotify || ev->type == PropertyNotify) { return True; } else { return False; }}void withdraw_window(Display *dpy, Window win){ XEvent ev; int n; int wm_state; int wait_for_wm_state = 1; int wait_for_reparent = 1; int wait_for_unmap = 1; Window root_return, parent_return; Window *children_return = NULL; unsigned int nchildren_return; wm_state = get_wm_state(dpy, win); if(wm_state <= 0) { // window is already withdrawn or no wm (supporting wm_state) is running wait_for_wm_state = 0; } if(XQueryTree(dpy, win, &root_return, &parent_return, &children_return, &nchildren_return)) { if(root_return == parent_return) { //no wm running or the wm is not reparenting // if the wm is not reparenting it should support wm_state wait_for_reparent = 0; } if(children_return) { XFree(children_return); } } else { WARNING("%s", "XQueryTree failed\n"); root_return = DefaultRootWindow(dpy); } XWithdrawWindow(dpy, win, DefaultScreen(dpy)); // Wait for window to be unmapped/withdrawn // If a wm is running wait for WM_STATE==withdrawn // or a reparent event (only works for a reparenting wm) // to know when the wm is done with our window // Always wait until we get an unmap notification. // Also wait ~0.5s for reparent/wmstate==withdraw else continue // and assume the wm is broken for(n = 0; wait_for_unmap || (n < 50 && (wait_for_reparent || wait_for_wm_state)); n++) { while(XCheckIfEvent(dpy, &ev, withdraw_predicate, NULL) == True) { switch(ev.type) { case UnmapNotify: wait_for_unmap = 0; break; case ReparentNotify: if(ev.xreparent.parent == root_return) { wait_for_reparent = 0; //we don't have to wait for wmstate also //wait_for_wm_state = 0; } break; case PropertyNotify: if(ev.xproperty.atom == wm_state_atom) { wm_state = get_wm_state(dpy, win); if(wm_state <= 0) { //window has been withdrawn wait_for_wm_state = 0; //don't wait for reparent now wait_for_reparent = 0; } } default: break; } } usleep(10000); } if(n == 50) { DNOTE("%s", "Withdraw window timeout, broken WM ?\n"); }}static void switch_to_fullscreen_state(Display *dpy, Window win){ XEvent ev; XEvent ret_ev; XWindowAttributes attrs; XWindowChanges win_changes; int x, y; int n; XSizeHints *sizehints; // We don't want to have to replace the window manually when remapping it sizehints = XAllocSizeHints(); sizehints->flags = USPosition | PWinGravity; sizehints->x = 0; // obsolete but should be set in case sizehints->y = 0; // there is an old wm used sizehints->win_gravity = NorthWestGravity; save_normal_geometry(dpy, win); if(!has_ewmh_state_fullscreen) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -