/* mcwm, a small window manager for the X Window System using the X * protocol C Binding libraries. * * For 'user' configurable stuff, see config.h. * * MC, mc at the domain hack.org * http://hack.org/mc/ * * Copyright (c) 2010, 2011, 2012 Michael Cardell Widerkrantz, mc at * the domain hack.org. * * Copyright (c) 2013 of the mcwm-beast patches Patrick Louis, Youri Mouton, * patrick at unixhub [dot] net * beastie at unixhub [dot] net * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "config.h" #ifdef DEBUG #include "events.h" #endif #ifdef DMALLOC #include "dmalloc.h" #endif #ifdef DEBUG #define PDEBUG(Args...) \ do { fprintf(stderr, "mcwm: "); fprintf(stderr, ##Args); } while(0) #define D(x) x #else #define PDEBUG(Args...) #define D(x) #endif ///---Internal Constants---/// #define MCWM_MOVE 2 // We're currently moving a window with the mouse. #define MCWM_RESIZE 3 // We're currently resizing a window with the mouse. #define MCWM_TABBING 4 // We're currently tabbing around the window list, looking for a new window to focus on. #define WORKSPACES 10 // Number of workspaces. #define NET_WM_FIXED 0xffffffff // Value in WM hint which means this window is fixed on all workspaces. #define MCWM_NOWS 0xfffffffe // This means we didn't get any window hint at all. ///---Types---/// typedef enum { // All our key shortcuts. KEY_F, KEY_H, KEY_J, KEY_K, KEY_L, KEY_M, KEY_R, KEY_RET, KEY_P, KEY_X, KEY_TAB, KEY_BACKTAB, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_Y, KEY_U, KEY_B, KEY_N, KEY_G, KEY_END, KEY_PREVSCR, KEY_NEXTSCR, KEY_ICONIFY, KEY_PREVWS, KEY_NEXTWS, KEY_GROW, KEY_SHRINK, KEY_UNKILLABLE, KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_MAX } key_enum_t; struct monitor { xcb_randr_output_t id; char *name; int16_t x; // X and Y. int16_t y; uint16_t width; // Width in pixels. uint16_t height; // Height in pixels. struct item *item; // Pointer to our place in output list. }; struct sizepos { int16_t x; int16_t y; uint16_t width; uint16_t height; }; struct client { // Everything we know about a window. xcb_drawable_t id; // ID of this window. bool usercoord; // X,Y was set by -geom. int16_t x; // X coordinate. int16_t y; // Y coordinate. uint16_t width; // Width in pixels. uint16_t height; // Height in pixels. struct sizepos origsize; // Original size if we're currently maxed. uint16_t min_width, min_height; // Hints from application. uint16_t max_width, max_height; int32_t width_inc, height_inc; int32_t base_width, base_height; bool fixed; // Visible on all workspaces? bool unkillable; // Make a window unkillable by MODKEY+Q. bool vertmaxed; // Vertically maximized? bool hormaxed; // horizontally maximized? bool maxed; // Totally maximized? bool verthor; // half horizontally maxed, full vert struct monitor *monitor; // The physical output this window is on. struct item *winitem; // Pointer to our place in global windows list. struct item *wsitem[WORKSPACES];// Pointer to our place in every workspace window list. }; struct winconf { // Window configuration data. int16_t x; int16_t y; uint16_t width; uint16_t height; uint8_t stackmode; xcb_window_t sibling; }; ///---Globals---/// char **user_argv; int sigcode; // Signal code. Non-zero if we've been interruped by a signal. xcb_connection_t *conn; // Connection to X server. xcb_screen_t *screen; // Our current screen. int randrbase; // Beginning of RANDR extension events. uint32_t curws = 0; // Current workspace. struct client *focuswin; // Current focus window. struct client *lastfocuswin; // Last focused window. NOTE! Only used to communicate between start and end of tabbing mode. struct item *winlist = NULL; // Global list of all client windows. struct item *monlist = NULL; // List of all physical monitor outputs. int mode = 0; // Internal mode, such as move or resize struct item *wslist[WORKSPACES] = { // Workspaces: Every workspace has a list of all visible windows. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; struct keys { // Shortcut key type and initializiation. xcb_keysym_t keysym; xcb_keycode_t keycode; } keys[KEY_MAX] = { { USERKEY_FIX, 0 }, { USERKEY_MOVE_LEFT, 0 }, { USERKEY_MOVE_DOWN, 0 }, { USERKEY_MOVE_UP, 0 }, { USERKEY_MOVE_RIGHT, 0 }, { USERKEY_MAXVERT, 0 }, { USERKEY_RAISE, 0 }, { USERKEY_TERMINAL, 0 }, { USERKEY_MENU, 0 }, { USERKEY_MAX, 0 }, { USERKEY_CHANGE, 0 }, { USERKEY_BACKCHANGE, 0 }, { USERKEY_WS1, 0 }, { USERKEY_WS2, 0 }, { USERKEY_WS3, 0 }, { USERKEY_WS4, 0 }, { USERKEY_WS5, 0 }, { USERKEY_WS6, 0 }, { USERKEY_WS7, 0 }, { USERKEY_WS8, 0 }, { USERKEY_WS9, 0 }, { USERKEY_WS10, 0 }, { USERKEY_TOPLEFT, 0 }, { USERKEY_TOPRIGHT, 0 }, { USERKEY_BOTLEFT, 0 }, { USERKEY_BOTRIGHT, 0 }, { USERKEY_CENTER, 0 }, { USERKEY_DELETE, 0 }, { USERKEY_PREVSCREEN, 0 }, { USERKEY_NEXTSCREEN, 0 }, { USERKEY_ICONIFY, 0 }, { USERKEY_PREVWS, 0 }, { USERKEY_NEXTWS, 0 }, { USERKEY_GROW, 0 }, { USERKEY_SHRINK, 0 }, { USERKEY_UNKILLABLE, 0}, { USERKEY_UP, 0}, { USERKEY_DOWN, 0}, { USERKEY_RIGHT, 0}, { USERKEY_LEFT, 0}, }; struct modkeycodes { // All keycodes generating our MODKEY mask. xcb_keycode_t *keycodes; unsigned len; } modkeys = { NULL, 0 }; ///---Global configuration.---/// struct conf { int borderwidth; // Do we draw borders for non-focused window? If so, how large? #ifdef DOUBLEBORDER int outer_border; // The size of the outer border #endif char *terminal; // Path to terminal to start. char *menu; // Path to menu to start. uint32_t focuscol; // Focused border colour. uint32_t unfocuscol; // Unfocused border colour. uint32_t fixedcol; // Fixed windows border colour. uint32_t unkillcol; // unkillable window color uint32_t empty_col; // color for when we don't know what to put uint32_t fixed_unkil_col; // unkillable and fixed window color uint32_t outer_border_col; // the color of the outer border bool allowicons; // Allow windows to be unmapped. } conf; xcb_atom_t atom_desktop; // hint that says hat workspace a window should be on xcb_atom_t atom_current_desktop; xcb_atom_t atom_unkillable; xcb_atom_t wm_delete_window; // WM_DELETE_WINDOW event to close windows xcb_atom_t wm_change_state; xcb_atom_t wm_state; xcb_atom_t wm_protocols; // WM_PROTOCOLS. ///---Functions prototypes---/// static void finishtabbing(void); void mcwm_restart(void); void mcwm_exit(void); static struct modkeycodes getmodkeys(xcb_mod_mask_t modmask); static void cleanup(const int code); void mcwm_exit(void); static void arrangewindows(void); static void setwmdesktop(xcb_drawable_t win, uint32_t ws); void maxverthor(struct client *client, bool right_left); static int32_t getwmdesktop(xcb_drawable_t win); static void addtoworkspace(struct client *client, uint32_t ws); static void delfromworkspace(struct client *client, uint32_t ws); void cursor_move(uint8_t direction, bool fast); static void changeworkspace(uint32_t ws); static void sendtoworkspace(struct client *client, uint32_t ws); static void fixwindow(struct client *client); static uint32_t getcolor(const char *hex); static void forgetclient(struct client *client); static void forgetwin(xcb_window_t win); static void fitonscreen(struct client *client); static void newwin(xcb_window_t win); static struct client *setupwin(xcb_window_t win); static xcb_keycode_t keysymtokeycode(xcb_keysym_t keysym, xcb_key_symbols_t *keysyms); static int setupkeys(void); static int setupscreen(void); static int setuprandr(void); static void getrandr(void); static void getoutputs(xcb_randr_output_t *outputs,const int len, xcb_timestamp_t timestamp); void arrbymon(struct monitor *monitor); static struct monitor *findmonitor(xcb_randr_output_t id); static struct monitor *findclones(xcb_randr_output_t id, int16_t x, int16_t y); static struct monitor *findmonbycoord(int16_t x, int16_t y); static void delmonitor(struct monitor *mon); static struct monitor *addmonitor(xcb_randr_output_t id, char *name, uint32_t x, uint32_t y, uint16_t width, uint16_t height); static void raisewindow(xcb_drawable_t win); static void raiseorlower(struct client *client); static void movelim(struct client *client); static void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y); static struct client *findclient(xcb_drawable_t win); static void focusnext(const bool reverse); static void setunfocus(struct client *client); static void setfocus(struct client *client); static int start(char *program); static void resizelim(struct client *client); static void resize(xcb_drawable_t win, uint16_t width, uint16_t height); static void resizestep(struct client *client, char direction); static void resizestepslow(struct client *client, char direction); void resizestep_keep_aspect(struct client *client, char direction); static void mousemove(struct client *client, int rel_x, int rel_y); static void mouseresize(struct client *client, int rel_x, int rel_y,bool accept_resize); static void movestep(struct client *client, char direction); static void movestepslow(struct client *client, char direction); static void setborders(struct client *client,bool isitfocused); static void unmax(struct client *client); static void maximize(struct client *client); static void maxvert(struct client *client); static void maxhor(struct client *client); static void hide(struct client *client); static bool getpointer(xcb_drawable_t win, int16_t *x, int16_t *y); static bool getgeom(xcb_drawable_t win, int16_t *x, int16_t *y, uint16_t *width, uint16_t *height); static void topleft(void); static void topright(void); static void botleft(void); static void botright(void); static void center(void); static void deletewin(void); static void prevscreen(void); static void nextscreen(void); static void handle_keypress(xcb_key_press_event_t *ev); static void configwin(xcb_window_t win, uint16_t mask, struct winconf wc); static void configurerequest(xcb_configure_request_event_t *e); static void events(void); static void printhelp(void); static void sigcatch(const int sig); static xcb_atom_t getatom(char *atom_name); ///---Function bodies---/// void finishtabbing(void) // MODKEY was released after tabbing around the { // workspace window ring. This means this mode is // finished and we have found a new focus window. // We need to move first the window we used to focus // on to the head of the window list and then move the // new focus to the head of the list as well. The list // should always start with the window we're focusing on. mode = 0; if (NULL != lastfocuswin) { movetohead(&wslist[curws], lastfocuswin->wsitem[curws]); lastfocuswin = NULL; } movetohead(&wslist[curws], focuswin->wsitem[curws]); } struct modkeycodes getmodkeys(xcb_mod_mask_t modmask) { // Find out what keycode modmask is bound to. Returns a struct. If the // len in the struct is 0 something went wrong. xcb_get_modifier_mapping_cookie_t cookie; xcb_get_modifier_mapping_reply_t *reply; xcb_keycode_t *modmap; struct modkeycodes keycodes = { NULL, 0 }; int mask; unsigned i; const xcb_mod_mask_t masks[8] = { XCB_MOD_MASK_SHIFT, XCB_MOD_MASK_LOCK, XCB_MOD_MASK_CONTROL, XCB_MOD_MASK_1, XCB_MOD_MASK_2, XCB_MOD_MASK_3, XCB_MOD_MASK_4, XCB_MOD_MASK_5 }; cookie = xcb_get_modifier_mapping_unchecked(conn); if ((reply = xcb_get_modifier_mapping_reply(conn, cookie, NULL)) == NULL) return keycodes; if (NULL == (keycodes.keycodes = calloc(reply->keycodes_per_modifier, sizeof(xcb_keycode_t)))) { PDEBUG("Out of memory.\n"); return keycodes; } modmap = xcb_get_modifier_mapping_keycodes(reply); /* The modmap now contains keycodes. * The number of keycodes in the list is 8 * * keycodes_per_modifier. The keycodes are divided into eight * sets, with each set containing keycodes_per_modifier elements. * Each set corresponds to a modifier in masks[] in the order * specified above. * The keycodes_per_modifier value is chosen arbitrarily by the * server. Zeroes are used to fill in unused elements within each set. */ for (mask = 0; mask < 8; mask ++) { if (masks[mask] == modmask) { for (i = 0; i < reply->keycodes_per_modifier; i ++) { if (0 != modmap[mask * reply->keycodes_per_modifier + i]) { keycodes.keycodes[i] = modmap[mask * reply->keycodes_per_modifier + i]; keycodes.len ++; } } PDEBUG("Got %d keycodes.\n", keycodes.len); } } /* for mask */ free(reply); return keycodes; } void cleanup(const int code) // Set keyboard focus to follow mouse pointer. Then exit. { //We don't need to bother mapping all windows we know about. They //should all be in the X server's Save Set and should be mapped automagically. xcb_set_input_focus(conn, XCB_NONE, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_CURRENT_TIME); xcb_flush(conn); xcb_disconnect(conn); exit(code); } void arrangewindows(void) //Rearrange windows to fit new screen size. { struct item *item; struct client *client; /* Go through all windows. If they don't fit on the new screen, * move them around and resize them as necessary. */ for (item = winlist; item != NULL; item = item->next) { client = item->data; fitonscreen(client); } /* for */ } void setwmdesktop(xcb_drawable_t win, uint32_t ws) { // Set the EWMH hint that window win belongs on workspace ws. PDEBUG("Changing _NET_WM_DESKTOP on window %d to %d\n", win, ws); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, atom_desktop, XCB_ATOM_CARDINAL, 32, 1, &ws); } int32_t getwmdesktop(xcb_drawable_t win) { // Get EWWM hint so we might know what workspace window win should be visible on. // Returns either workspace, NET_WM_FIXED if this window should be // visible on all workspaces or MCWM_NOWS if we didn't find any hints. xcb_get_property_reply_t *reply; xcb_get_property_cookie_t cookie; uint32_t *wsp; uint32_t ws; cookie = xcb_get_property(conn, false, win, atom_desktop, XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(int32_t)); reply = xcb_get_property_reply(conn, cookie, NULL); if (NULL == reply) { fprintf(stderr, "mcwm: Couldn't get properties for win %d\n", win); return MCWM_NOWS; } /* Length is 0 if we didn't find it. */ if (0 == xcb_get_property_value_length(reply)) { PDEBUG("_NET_WM_DESKTOP reply was 0 length.\n"); free(reply); return MCWM_NOWS; } wsp = xcb_get_property_value(reply); ws = *wsp; PDEBUG("got _NET_WM_DESKTOP: %d stored at %p.\n", ws, (void *)wsp); free(reply); return ws; } bool get_unkil_state(xcb_drawable_t win) { // check if the window is unkillable, if yes return true xcb_get_property_reply_t *reply; xcb_get_property_cookie_t cookie; uint32_t *wsp; uint32_t ws; cookie = xcb_get_property(conn, false, win, atom_unkillable, XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(int8_t)); reply = xcb_get_property_reply(conn, cookie, NULL); if (NULL == reply) return false; if (0 == xcb_get_property_value_length(reply)){ free(reply); return false; } wsp = xcb_get_property_value(reply); ws = * wsp; free(reply); if (ws == 1) return true; else return false; } void addtoworkspace(struct client *client, uint32_t ws) { // Add a window, specified by client, to workspace ws. struct item *item; item = additem(&wslist[ws]); if (NULL == item) { PDEBUG("addtoworkspace: Out of memory.\n"); return; } /* Remember our place in the workspace window list. */ client->wsitem[ws] = item; /* Remember the data. */ item->data = client; /* Set window hint property so we can survive a crash. * Fixed windows have their own special WM hint. We don't want to * mess with that. */ if (!client->fixed) setwmdesktop(client->id, ws); } void delfromworkspace(struct client *client, uint32_t ws) { // Delete window client from workspace ws. delitem(&wslist[ws], client->wsitem[ws]); /* Reset our place in the workspace window list. */ client->wsitem[ws] = NULL; } void changeworkspace(uint32_t ws) // Change current workspace to ws. { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screen->root, atom_current_desktop, XCB_ATOM_CARDINAL, 32, 1,&ws); struct item *item; struct client *client; if (ws == curws) { PDEBUG("Changing to same workspace!\n"); return; } PDEBUG("Changing from workspace #%d to #%d\n", curws, ws); /* Go through list of current ws. Unmap everything that isn't fixed. */ for (item = wslist[curws]; item != NULL; item = item->next) { client = item->data; PDEBUG("changeworkspace. unmap phase. ws #%d, client-fixed: %d\n", curws, client->fixed); if (!client->fixed) /* This is an ordinary window. Just unmap it. Note that * this will generate an unnecessary UnmapNotify event * which we will try to handle later.*/ xcb_unmap_window(conn, client->id); } /* for */ /* Go through list of new ws. Map everything that isn't fixed. */ for (item = wslist[ws]; item != NULL; item = item->next) { client = item->data; PDEBUG("changeworkspace. map phase. ws #%d, client-fixed: %d\n", ws, client->fixed); /* Fixed windows are already mapped. Map everything else. */ if (!client->fixed) xcb_map_window(conn, client->id); } xcb_flush(conn); curws = ws; } void fixwindow(struct client *client) { // Fix or unfix a window client from all workspaces. If setcolour is // set, also change back to ordinary focus colour when unfixing. uint32_t ws; if (NULL == client) return; if (client->fixed) { client->fixed = false; setwmdesktop(client->id, curws); /* Delete from all workspace lists except current. */ for (ws = 0; ws < WORKSPACES; ws ++) if (ws != curws) delfromworkspace(client, ws); } else { /* * First raise the window. If we're going to another desktop * we don't want this fixed window to be occluded behind * something else. */ raisewindow(client->id); client->fixed = true; setwmdesktop(client->id, NET_WM_FIXED); /* Add window to all workspace lists. */ for (ws = 0; ws < WORKSPACES; ws ++) if (ws != curws) addtoworkspace(client, ws); } setborders(client,true); xcb_flush(conn); } void unkillablewindow(struct client *client) { //Make unkillable or killable a window client. If setcolour is // set, also change back to ordinary focus colour when *unkillabilling*. if (NULL == client) return; if (client->unkillable) { client->unkillable = false; xcb_delete_property(conn, client->id, atom_unkillable); } else { /* First raise the window and make it unkillable. */ raisewindow(client->id); client->unkillable = true; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->id, atom_unkillable , XCB_ATOM_CARDINAL, 8, 1, &client->unkillable); } setborders(client,true); xcb_flush(conn); } void sendtoworkspace(struct client *client, uint32_t ws) { if (NULL == client) return; if (client->fixed || ws == curws) return; addtoworkspace(client, ws); delfromworkspace(client, curws); xcb_unmap_window(conn, client->id); xcb_flush(conn); } uint32_t getcolor(const char *hex) // Get the pixel values of a named colour colstr. { // Returns pixel values. char strgroups[3][3] = {{hex[1], hex[2], '\0'}, {hex[3], hex[4], '\0'}, {hex[5], hex[6], '\0'} }; uint32_t rgb16[3] = {(strtol(strgroups[0], NULL, 16)), (strtol(strgroups[1], NULL, 16)), (strtol(strgroups[2], NULL, 16)) }; return (rgb16[0] << 16) + (rgb16[1] << 8) + rgb16[2]; } void forgetclient(struct client *client) { // Forget everything about client client. uint32_t ws; if (NULL == client) { PDEBUG("forgetclient: client was NULL\n"); return; } /* Delete this client from whatever workspace lists it belongs to. * Note that it's OK to be on several workspaces at once even if * you're not fixed. */ for (ws = 0; ws < WORKSPACES; ws ++) if (NULL != client->wsitem[ws]) delfromworkspace(client, ws); /* Remove from global window list. */ freeitem(&winlist, NULL, client->winitem); } void forgetwin(xcb_window_t win) // Forget everything about a client with client->id win. { struct item *item; struct client *client; /* Find this window in the global window list. */ for (item = winlist; item != NULL; item = item->next) { client = item->data; /* Forget about it completely and free allocated data. * Note that it might already be freed by handling an * UnmapNotify, so it isn't necessarily an error if we don't find it. */ PDEBUG("Win %d == client ID %d\n", win, client->id); if (win == client->id) { /* Found it. */ PDEBUG("Found it. Forgetting...\n"); /* Delete window from whatever workspace lists it belonged * to. Note that it's OK to be on several workspaces at * once. */ forgetclient(client); return; } } } void fitonscreen(struct client *client) { // Fit client on physical screen, moving and resizing as necessary. int16_t mon_x; int16_t mon_y; uint16_t mon_width; uint16_t mon_height; bool willmove = false; bool willresize = false; client->vertmaxed = false; if (client->maxed) { client->maxed = false; setborders(client,false); } if (NULL == client->monitor) { /* This window isn't attached to any physical monitor. This * probably means there is no RANDR, so we use the root window size. */ mon_x = 0; mon_y = 0; mon_width = screen->width_in_pixels; mon_height = screen->height_in_pixels; } else { mon_x = client->monitor->x; mon_y = client->monitor->y; mon_width = client->monitor->width; mon_height = client->monitor->height; } PDEBUG("Is window outside monitor?\n"); PDEBUG("x: %d between %d and %d?\n", client->x, mon_x, mon_x + mon_width); PDEBUG("y: %d between %d and %d?\n", client->y, mon_y, mon_y + mon_height); /* Is it outside the physical monitor? */ if (client->x > mon_x + mon_width) { client->x = mon_x + mon_width - client->width; willmove = true; } if (client->y > mon_y + mon_height) { client->y = mon_y + mon_height - client->height; willmove = true; } if (client->x < mon_x) { client->x = mon_x; willmove = true; } if (client->y < mon_y) { client->y = mon_y; willmove = true; } /* Is it smaller than it wants to be? */ if (0 != client->min_height && client->height < client->min_height) { client->height = client->min_height; willresize = true; } if (0 != client->min_width && client->width < client->min_width) { client->width = client->min_width; willresize = true; } /* If the window is larger than our screen, just place it in the * corner and resize. */ if (client->width + conf.borderwidth * 2 > mon_width) { client->x = mon_x; client->width = mon_width - conf.borderwidth * 2;; willmove = true; willresize = true; } else if (client->x + client->width + conf.borderwidth * 2 > mon_x + mon_width) { client->x = mon_x + mon_width - (client->width + conf.borderwidth * 2); willmove = true; } if (client->height + conf.borderwidth * 2 > mon_height) { client->y = mon_y; client->height = mon_height - conf.borderwidth * 2; willmove = true; willresize = true; } else if (client->y + client->height + conf.borderwidth * 2 > mon_y + mon_height) { client->y = mon_y + mon_height - (client->height + conf.borderwidth * 2); willmove = true; } if (willmove) { PDEBUG("Moving to %d,%d.\n", client->x, client->y); movewindow(client->id, client->x, client->y); } if (willresize) { PDEBUG("Resizing to %d x %d.\n", client->width, client->height); resize(client->id, client->width, client->height); } } void newwin(xcb_window_t win) // Set position, geometry and attributes of a new window and show it { // on the screen. struct client *client; if (NULL != findclient(win)) { /* We know this window from before. It's trying to map itself * on the current workspace, but since it's unmapped it * probably belongs on another workspace. We don't like that. */ return; } /* Set up stuff, like borders, add the window to the client list, et cetera. */ client = setupwin(win); if (NULL == client) { fprintf(stderr, "mcwm: Couldn't set up window. Out of memory.\n"); return; } /* Add this window to the current workspace. */ addtoworkspace(client, curws); /* If the client doesn't say the user specified the coordinates * for the window we map it where our pointer is instead. */ if (!client->usercoord) { int16_t pointx; int16_t pointy; PDEBUG("Coordinates not set by user. Using pointer: %d,%d.\n", pointx, pointy); /* Get pointer position so we can move the window to the cursor. */ if (!getpointer(screen->root, &pointx, &pointy)) { PDEBUG("Failed to get pointer coords!\n"); pointx = 0; pointy = 0; } client->x = pointx; client->y = pointy; movewindow(client->id, client->x, client->y); } else PDEBUG("User set coordinates.\n"); /* Find the physical output this window will be on if RANDR is active. */ if (-1 != randrbase) { client->monitor = findmonbycoord(client->x, client->y); if (NULL == client->monitor) { /* Window coordinates are outside all physical monitors. * Choose the first screen.*/ if (NULL != monlist) client->monitor = monlist->data; } } fitonscreen(client); /* Show window on screen. */ xcb_map_window(conn, client->id); /* Declare window normal. */ long data[] = { XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE }; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->id, wm_state, wm_state, 32, 2, data); /* Move cursor into the middle of the window so we don't lose the * pointer to another window. */ xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0, client->width / 2, client->height / 2); xcb_flush(conn); } struct client *setupwin(xcb_window_t win) { // Set border colour, width and event mask for window. uint32_t mask = 0; uint32_t values[2]; struct item *item; struct client *client; xcb_size_hints_t hints; uint32_t ws; /* Don't populate the back of windows with bad redraw of the last frame * Use the background frame instead. * This is really good for videos because we keep their aspect ratio. */ if (EMPTY_COL=="0") { values[0] = 1; xcb_change_window_attributes(conn, win, XCB_BACK_PIXMAP_PARENT_RELATIVE, values); } else { values[0] = conf.empty_col; xcb_change_window_attributes(conn, win, XCB_CW_BACK_PIXEL, values); } /* Set border width, for the first time. */ values[0] = conf.borderwidth; mask = XCB_CONFIG_WINDOW_BORDER_WIDTH; xcb_configure_window(conn, win, mask, values); mask = XCB_CW_EVENT_MASK; values[0] = XCB_EVENT_MASK_ENTER_WINDOW; xcb_change_window_attributes_checked(conn, win, mask, values); /* Add this window to the X Save Set. */ xcb_change_save_set(conn, XCB_SET_MODE_INSERT, win); xcb_flush(conn); /* Remember window and store a few things about it. */ item = additem(&winlist); if (NULL == item) { PDEBUG("newwin: Out of memory.\n"); return NULL; } client = malloc(sizeof(struct client)); if (NULL == client) { PDEBUG("newwin: Out of memory.\n"); return NULL; } item->data = client; /* Initialize client. */ client->id = win; client->usercoord = false; client->x = 0; client->y = 0; client->width = 0; client->height = 0; client->min_width = 0; client->min_height = 0; client->max_width = screen->width_in_pixels; client->max_height = screen->height_in_pixels; client->base_width = 0; client->base_height = 0; client->width_inc = 1; client->height_inc = 1; client->vertmaxed = false; client->hormaxed = false; client->maxed = false; client->monitor = NULL; client->winitem = item; client->unkillable=false; client->fixed = false; for (ws = 0; ws < WORKSPACES; ws ++) client->wsitem[ws] = NULL; PDEBUG("Adding window %d\n", client->id); /* Get window geometry. */ if (!getgeom(client->id, &client->x, &client->y, &client->width, &client->height)) fprintf(stderr, "Couldn't get geometry in initial setup of window.\n"); /* Get the window's incremental size step, if any.*/ if (!xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints_unchecked(conn, win), &hints, NULL)) PDEBUG("Couldn't get size hints.\n"); /* The user specified the position coordinates. Remember that so * we can use geometry later. */ if (hints.flags &XCB_ICCCM_SIZE_HINT_US_POSITION) client->usercoord = true; if (hints.flags &XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) { client->min_width = hints.min_width; client->min_height = hints.min_height; } if (hints.flags &XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) { client->max_width = hints.max_width; client->max_height = hints.max_height; } if (hints.flags &XCB_ICCCM_SIZE_HINT_P_RESIZE_INC) { client->width_inc = hints.width_inc; client->height_inc = hints.height_inc; PDEBUG("widht_inc %d\nheight_inc %d\n", client->width_inc, client->height_inc); } if (hints.flags &XCB_ICCCM_SIZE_HINT_BASE_SIZE) { client->base_width = hints.base_width; client->base_height = hints.base_height; } return client; } xcb_keycode_t keysymtokeycode(xcb_keysym_t keysym, xcb_key_symbols_t *keysyms) { // Get a keycode from a keysym. Returns keycode value. xcb_keycode_t *keyp; xcb_keycode_t key; /* We only use the first keysymbol, even if there are more. */ keyp = xcb_key_symbols_get_keycode(keysyms, keysym); if (NULL == keyp) { fprintf(stderr, "mcwm: Couldn't look up key. Exiting.\n"); exit(1); return 0; } key = *keyp; free(keyp); return key; } int setupkeys(void) //Set up all shortcut keys. { // Returns 0 on success, non-zero otherwise. xcb_key_symbols_t *keysyms; unsigned i; /* Get all the keysymbols. */ keysyms = xcb_key_symbols_alloc(conn); /* Find out what keys generates our MODKEY mask. Unfortunately it * might be several keys. */ if (NULL != modkeys.keycodes) { free(modkeys.keycodes); } modkeys = getmodkeys(MODKEY); if (0 == modkeys.len) { fprintf(stderr, "We couldn't find any keycodes to our main modifier " "key!\n"); return -1; } for (i = 0; i < modkeys.len; i ++) { /* Grab the keys that are bound to MODKEY mask with any other modifier.*/ xcb_grab_key(conn, 1, screen->root, XCB_MOD_MASK_ANY, modkeys.keycodes[i], XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); } /* Now grab the rest of the keys with the MODKEY modifier. */ for (i = KEY_F; i < KEY_MAX; i ++) { if (XK_VoidSymbol == keys[i].keysym) { keys[i].keycode = 0; continue; } keys[i].keycode = keysymtokeycode(keys[i].keysym, keysyms); if (0 == keys[i].keycode) { /* Couldn't set up keys! */ /* Get rid of key symbols. */ xcb_key_symbols_free(keysyms); return -1; } /* Grab other keys with a modifier mask. */ xcb_grab_key(conn, 1, screen->root, MODKEY, keys[i].keycode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); /* XXX Also grab it's shifted counterpart. A bit ugly here * because we grab all of them not just the ones we want. */ xcb_grab_key(conn, 1, screen->root, MODKEY | SHIFTMOD, keys[i].keycode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); xcb_grab_key(conn, 1, screen->root, MODKEY | CONTROLMOD, keys[i].keycode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); } /* for */ /* Need this to take effect NOW! */ xcb_flush(conn); /* Get rid of the key symbols table. */ xcb_key_symbols_free(keysyms); return 0; } int setupscreen(void) //Walk through all existing windows and set them up. { // Returns 0 on success. xcb_query_tree_reply_t *reply; xcb_query_pointer_reply_t *pointer; int i; int len; xcb_window_t *children; xcb_get_window_attributes_reply_t *attr; struct client *client; uint32_t ws; /* Get all children. */ reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, screen->root), 0); if (NULL == reply) return -1; len = xcb_query_tree_children_length(reply); children = xcb_query_tree_children(reply); /* Set up all windows on this root. */ for (i = 0; i < len; i ++) { attr = xcb_get_window_attributes_reply( conn, xcb_get_window_attributes(conn, children[i]), NULL); if (!attr) { fprintf(stderr, "Couldn't get attributes for window %d.", children[i]); continue; } /* Don't set up or even bother windows in override redirect mode. * This mode means they wouldn't have been reported to us * with a MapRequest if we had been running, so in the * normal case we wouldn't have seen them. * Only handle visible windows. */ if (!attr->override_redirect && attr->map_state == XCB_MAP_STATE_VIEWABLE) { client = setupwin(children[i]); setborders(client,false); if (NULL != client) { /*cFind the physical output this window will be on if * RANDR is active. */ if (-1 != randrbase) { PDEBUG("Looking for monitor on %d x %d.\n", client->x, client->y); client->monitor = findmonbycoord(client->x, client->y); #if DEBUG if (NULL != client->monitor) PDEBUG("Found client on monitor %s.\n", client->monitor->name); else PDEBUG("Couldn't find client on any monitor.\n"); #endif } /* Fit window on physical screen. */ fitonscreen(client); /* Check if this window has a workspace set already as a WM hint. */ ws = getwmdesktop(children[i]); if (get_unkil_state(children[i])) unkillablewindow(client); if (ws == NET_WM_FIXED) { /* Add to current workspace. */ addtoworkspace(client, curws); /* Add to all other workspaces. */ fixwindow(client); } else if (MCWM_NOWS != ws && ws < WORKSPACES) { addtoworkspace(client, ws); /* If it's not our current workspace, hide it. */ if (ws != curws) xcb_unmap_window(conn, client->id); } else /* No workspace hint at all. Just add it to our current workspace. */ addtoworkspace(client, curws); } } free(attr); } /* for */ changeworkspace(0); /* Get pointer position so we can set focus on any window which * might be under it. */ pointer = xcb_query_pointer_reply( conn, xcb_query_pointer(conn, screen->root), 0); if (NULL == pointer) focuswin = NULL; else { setfocus(findclient(pointer->child)); free(pointer); } xcb_flush(conn); free(reply); return 0; } int setuprandr(void) // Set up RANDR extension. Get the extension base and subscribe to { // events. const xcb_query_extension_reply_t *extension; int base; extension = xcb_get_extension_data(conn, &xcb_randr_id); if (!extension->present) { printf("No RANDR extension.\n"); return -1; } else getrandr(); base = extension->first_event; PDEBUG("randrbase is %d.\n", base); xcb_randr_select_input(conn, screen->root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); xcb_flush(conn); return base; } void getrandr(void) // Get RANDR resources and figure out how many outputs there are. { xcb_randr_get_screen_resources_current_cookie_t rcookie; xcb_randr_get_screen_resources_current_reply_t *res; xcb_randr_output_t *outputs; int len; xcb_timestamp_t timestamp; rcookie = xcb_randr_get_screen_resources_current(conn, screen->root); res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL); if (NULL == res) { printf("No RANDR extension available.\n"); return; } timestamp = res->config_timestamp; len = xcb_randr_get_screen_resources_current_outputs_length(res); outputs = xcb_randr_get_screen_resources_current_outputs(res); PDEBUG("Found %d outputs.\n", len); /* Request information for all outputs. */ getoutputs(outputs, len, timestamp); free(res); } void getoutputs(xcb_randr_output_t *outputs, const int len, xcb_timestamp_t timestamp) { // Walk through all the RANDR outputs (number of outputs == len) there // was at time timestamp. char *name; xcb_randr_get_crtc_info_cookie_t icookie; xcb_randr_get_crtc_info_reply_t *crtc = NULL; xcb_randr_get_output_info_reply_t *output; struct monitor *mon; struct monitor *clonemon; xcb_randr_get_output_info_cookie_t ocookie[len]; int i; for (i = 0; i < len; i++) { ocookie[i] = xcb_randr_get_output_info(conn, outputs[i], timestamp); } /* Loop through all outputs. */ for (i = 0; i < len; i ++) { output = xcb_randr_get_output_info_reply(conn, ocookie[i], NULL); if (output == NULL) continue; asprintf(&name, "%.*s", xcb_randr_get_output_info_name_length(output), xcb_randr_get_output_info_name(output)); PDEBUG("Name: %s\n", name); PDEBUG("id: %d\n" , outputs[i]); PDEBUG("Size: %d x %d mm.\n", output->mm_width, output->mm_height); if (XCB_NONE != output->crtc) { icookie = xcb_randr_get_crtc_info(conn, output->crtc, timestamp); crtc = xcb_randr_get_crtc_info_reply(conn, icookie, NULL); if (NULL == crtc) return; PDEBUG("CRTC: at %d, %d, size: %d x %d.\n", crtc->x, crtc->y, crtc->width, crtc->height); /* Check if it's a clone. */ clonemon = findclones(outputs[i], crtc->x, crtc->y); if (NULL != clonemon) { PDEBUG("Monitor %s, id %d is a clone of %s, id %d. Skipping.\n", name, outputs[i], clonemon->name, clonemon->id); continue; } /* Do we know this monitor already? */ if (NULL == (mon = findmonitor(outputs[i]))) { PDEBUG("Monitor not known, adding to list.\n"); addmonitor(outputs[i], name, crtc->x, crtc->y, crtc->width, crtc->height); } else { bool changed = false; /* We know this monitor. Update information. If it's * smaller than before, rearrange windows. */ PDEBUG("Known monitor. Updating info.\n"); if (crtc->x != mon->x) { mon->x = crtc->x; changed = true; } if (crtc->y != mon->y) { mon->y = crtc->y; changed = true; } if (crtc->width != mon->width) { mon->width = crtc->width; changed = true; } if (crtc->height != mon->height) { mon->height = crtc->height; changed = true; } if (changed) arrbymon(mon); } free(crtc); } else { PDEBUG("Monitor not used at the moment.\n"); /* Check if it was used before. If it was, do something. */ if ((mon = findmonitor(outputs[i]))) { struct item *item; struct client *client; /* Check all windows on this monitor and move them to * the next or to the first monitor if there is no next. * FIXME: Use per monitor workspace list instead of * global window list. */ for (item = winlist; item != NULL; item = item->next) { client = item->data; if (client->monitor == mon) { if (NULL == client->monitor->item->next) { if (NULL == monlist) client->monitor = NULL; else client->monitor = monlist->data; } else client->monitor = client->monitor->item->next->data; fitonscreen(client); } } /* for */ /* It's not active anymore. Forget about it. */ delmonitor(mon); } } free(output); } /* for */ } void arrbymon(struct monitor *monitor) { struct item *item; struct client *client; PDEBUG("arrbymon\n"); /* Go through all windows on this monitor. If they don't fit on * the new screen, move them around and resize them as necessary. * FIXME: Use a per monitor workspace list instead of global * windows list. */ for (item = winlist; item != NULL; item = item->next) { client = item->data; if (client->monitor == monitor) fitonscreen(client); } /* for */ } struct monitor *findmonitor(xcb_randr_output_t id) { struct item *item; struct monitor *mon; for (item = monlist; item != NULL; item = item->next) { mon = item->data; if (id == mon->id) { PDEBUG("findmonitor: Found it. Output ID: %d\n", mon->id); return mon; } PDEBUG("findmonitor: Goint to %p.\n", item->next); } return NULL; } struct monitor *findclones(xcb_randr_output_t id, int16_t x, int16_t y) { struct monitor *clonemon; struct item *item; for (item = monlist; item != NULL; item = item->next) { clonemon = item->data; PDEBUG("Monitor %s: x, y: %d--%d, %d--%d.\n", clonemon->name, clonemon->x, clonemon->x + clonemon->width, clonemon->y, clonemon->y + clonemon->height); /* Check for same position. */ if (id != clonemon->id && clonemon->x == x && clonemon->y == y) return clonemon; } return NULL; } struct monitor *findmonbycoord(int16_t x, int16_t y) { struct item *item; struct monitor *mon; for (item = monlist; item != NULL; item = item->next) { mon = item->data; PDEBUG("Monitor %s: x, y: %d--%d, %d--%d.\n", mon->name, mon->x, mon->x + mon->width, mon->y, mon->y + mon->height); PDEBUG("Is %d,%d between them?\n", x, y); if (x >= mon->x && x <= mon->x + mon->width && y >= mon->y && y <= mon->y + mon->height) { PDEBUG("findmonbycoord: Found it. Output ID: %d, name %s\n", mon->id, mon->name); return mon; } } return NULL; } void delmonitor(struct monitor *mon) { PDEBUG("Deleting output %s.\n", mon->name); free(mon->name); freeitem(&monlist, NULL, mon->item); } struct monitor *addmonitor(xcb_randr_output_t id, char *name, uint32_t x, uint32_t y, uint16_t width, uint16_t height) { struct item *item; struct monitor *mon; if (NULL == (item = additem(&monlist))) { fprintf(stderr, "Out of memory.\n"); return NULL; } mon = malloc(sizeof(struct monitor)); if (NULL == mon) { fprintf(stderr, "Out of memory.\n"); return NULL; } item->data = mon; mon->id = id; mon->name = name; mon->x = x; mon->y = y; mon->width = width; mon->height = height; mon->item = item; return mon; } void raisewindow(xcb_drawable_t win)// Raise window win to top of stack. { uint32_t values[] = { XCB_STACK_MODE_ABOVE }; if (screen->root == win || 0 == win) return; xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_STACK_MODE, values); xcb_flush(conn); } void raiseorlower(struct client *client) { // Set window client to either top or bottom of stack depending on // where it is now. uint32_t values[] = { XCB_STACK_MODE_OPPOSITE }; xcb_drawable_t win; if (NULL == client) return; win = client->id; xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_STACK_MODE, values); xcb_flush(conn); } void movelim(struct client *client) { int16_t mon_y; uint16_t mon_height; if (NULL == client->monitor) { mon_y = 0; mon_height = screen->height_in_pixels; } else { mon_y = client->monitor->y; mon_height = client->monitor->height; } /* Is it outside the physical monitor? */ if (client->y < mon_y) { client->y = mon_y; } if (client->y + client->height > mon_y + mon_height - conf.borderwidth * 2) client->y = (mon_y + mon_height - conf.borderwidth * 2) - client->height; movewindow(client->id, client->x, client->y); } void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y) { // Move window win to root coordinates x,y. uint32_t values[2]; if (screen->root == win || 0 == win) return; values[0] = x; values[1] = y; xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values); xcb_flush(conn); } void focusnext(const bool reverse) // Change focus to next in window ring. { struct client *client = NULL; #if DEBUG if (NULL != focuswin) PDEBUG("Focus now in win %d\n", focuswin->id); #endif if (NULL == wslist[curws]) { PDEBUG("No windows to focus on in this workspace.\n"); return; } if (MCWM_TABBING != mode) { /* Remember what we last focused on. We need this when the * MODKEY is released and we move the last focused window in * the tabbing order list. */ lastfocuswin = focuswin; mode = MCWM_TABBING; PDEBUG("Began tabbing.\n"); } /* If we currently have no focus focus first in list. */ if (NULL == focuswin || NULL == focuswin->wsitem[curws]) { PDEBUG("Focusing first in list: %p\n", wslist[curws]); client = wslist[curws]->data; if (NULL != focuswin && NULL == focuswin->wsitem[curws]) PDEBUG("XXX Our focused window %d isn't on this workspace!\n", focuswin->id); } else { if (reverse) { if (NULL == focuswin->wsitem[curws]->prev) { /* We were at the head of list. Focusing on last * window in list unless we were already there. */ struct item *last = wslist[curws]; while (NULL != last->next) last = last->next; if (focuswin->wsitem[curws] != last->data) { PDEBUG("Beginning of list. Focusing last in list: %p\n", last); client = last->data; } } else { /* Otherwise, focus the next in list. */ PDEBUG("Tabbing. Focusing next: %p.\n", focuswin->wsitem[curws]->prev); client = focuswin->wsitem[curws]->prev->data; } } else { if (NULL == focuswin->wsitem[curws]->next) { /* We were at the end of list. Focusing on first window in * list unless we were already there. */ if (focuswin->wsitem[curws] != wslist[curws]->data) { PDEBUG("End of list. Focusing first in list: %p\n", wslist[curws]); client = wslist[curws]->data; } } else { /* Otherwise, focus the next in list. */ PDEBUG("Tabbing. Focusing next: %p.\n", focuswin->wsitem[curws]->next); client = focuswin->wsitem[curws]->next->data; } } } /* if NULL focuswin */ if (NULL != client) { /* Raise window if it's occluded, then warp pointer into it and * set keyboard focus to it. */ uint32_t values[] = { XCB_STACK_MODE_TOP_IF }; xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_STACK_MODE, values); xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, client->width / 2, client->height / 2); setfocus(client); } } void setunfocus(struct client *client) { // Mark window win as unfocused. if (NULL == focuswin) return; if (focuswin->id == screen->root) return; /* Set new border colour. */ setborders(client,false); xcb_flush(conn); } struct client *findclient(xcb_drawable_t win) { // Find client with client->id win in global window list. // Returns client pointer or NULL if not found. struct item *item; struct client *client; for (item = winlist; item != NULL; item = item->next) { client = item->data; if (win == client->id) { PDEBUG("findclient: Found it. Win: %d\n", client->id); return client; } } return NULL; } void setfocus(struct client *client)// Set focus on window client. { /* If client is NULL, we focus on whatever the pointer is on. * This is a pathological case, but it will make the poor user * able to focus on windows anyway, even though this window * manager might be buggy. */ if (NULL == client) { PDEBUG("setfocus: client was NULL!\n"); focuswin = NULL; xcb_set_input_focus(conn, XCB_NONE, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_CURRENT_TIME); xcb_flush(conn); return; } /* Don't bother focusing on the root window or on the same window * that already has focus. */ if (client->id == screen->root || client == focuswin) return; /* Unset last focus. */ if (NULL != focuswin) setunfocus(focuswin); /* Set new input focus. */ xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->id, XCB_CURRENT_TIME); xcb_flush(conn); /* Remember the new window as the current focused window. */ focuswin = client; setborders(client,true); } int start(char *program) { pid_t pid; pid = fork(); if (-1 == pid) { perror("fork"); return -1; } else if (0 == pid) { char *argv[2]; /* In the child. */ /* Make this process a new process leader, otherwise the * terminal will die when the wm dies. Also, this makes any * SIGCHLD go to this process when we fork again. */ if (-1 == setsid()) { perror("setsid"); exit(1); } argv[0] = program; argv[1] = NULL; if (-1 == execvp(program, argv)) { perror("execve"); exit(1); } exit(0); } return 0; } void resizelim(struct client *client) { // Resize with limit. int16_t mon_x; int16_t mon_y; uint16_t mon_width; uint16_t mon_height; if (NULL == client->monitor) { mon_x = 0; mon_y = 0; mon_width = screen->width_in_pixels; mon_height = screen->height_in_pixels; } else { mon_x = client->monitor->x; mon_y = client->monitor->y; mon_width = client->monitor->width; mon_height = client->monitor->height; } /* Is it smaller than it wants to be? */ if (0 != client->min_height && client->height < client->min_height) { client->height = client->min_height; } if (0 != client->min_width && client->width < client->min_width) { client->width = client->min_width; } if (client->x + client->width + conf.borderwidth * 2 > mon_x + mon_width) { client->width = mon_width - ((client->x - mon_x) + conf.borderwidth * 2); } if (client->y + client->height + conf.borderwidth * 2 > mon_y + mon_height) { client->height = mon_height - ((client->y - mon_y) + conf.borderwidth * 2); } resize(client->id, client->width, client->height); #ifdef DOUBLEBORDER setborders(client,true); #endif } void moveresize(xcb_drawable_t win, uint16_t x, uint16_t y, uint16_t width, uint16_t height) { uint32_t values[4]; if (screen->root == win || 0 == win) { /* Can't move or resize root. */ return; } PDEBUG("Moving to %d, %d, resizing to %d x %d.\n", x, y, width, height); values[0] = x; values[1] = y; values[2] = width; values[3] = height; xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); xcb_flush(conn); } void resize(xcb_drawable_t win, uint16_t width, uint16_t height) { // Resize window win to width,height. uint32_t values[2]; if (screen->root == win || 0 == win) return; PDEBUG("Resizing to %d x %d.\n", width, height); values[0] = width; values[1] = height; xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); xcb_flush(conn); } void resizestep(struct client *client, char direction) { // Resize window client in direction direction. Direction is: int step_x = MOVE_STEP; int step_y = MOVE_STEP; if (NULL == client) { return; } if (client->maxed) return; raisewindow(client->id); switch (direction) { case 'h': client->width = client->width - step_x; break; case 'j': client->height = client->height + step_y; break; case 'k': client->height = client->height - step_y; break; case 'l': client->width = client->width + step_x; break; default : PDEBUG("resizestep in unknown direction.\n"); break; } /* switch direction */ resizelim(client); if (client->vertmaxed) client->vertmaxed = false; if (client->hormaxed) client->hormaxed = false; xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, client->width / 2, client->height / 2); xcb_flush(conn); } void resizestepslow(struct client *client, char direction) { if (NULL == client) { return; } if (client->maxed) return; raisewindow(client->id); switch (direction) { case 'h': client->width = client->width -MOVE_STEP_SLOW ; break; case 'j': client->height = client->height + MOVE_STEP_SLOW; break; case 'k': client->height = client->height - MOVE_STEP_SLOW; break; case 'l': client->width = client->width + MOVE_STEP_SLOW; break; default : PDEBUG("resizestepslow in unknown direction.\n"); break; } /* switch direction */ resizelim(client); if (client->vertmaxed) client->vertmaxed = false; if (client->hormaxed) client->hormaxed = false; xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, client->width / 2, client->height / 2); xcb_flush(conn); } void resizestep_keep_aspect(struct client *client, char direction) { // Resize window and keep it's aspect ratio // The problem here is that it will exponentially grow the window if (NULL == client) return; if (client->maxed) return; raisewindow(client->id); switch (direction) { case 'h': client->width = client->width / 1.03; client->height = client->height / 1.03; break; case 'l': client->height = client->height * 1.03; client->width = client->width * 1.03; break; default : PDEBUG("resizestep in unknown direction.\n"); break; } /* switch direction */ resizelim(client); if (client->vertmaxed) client->vertmaxed = false; if (client->hormaxed) client->hormaxed =false; xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, client->width / 2, client->height / 2); xcb_flush(conn); } void mousemove(struct client *client, int rel_x, int rel_y) { // Move window win as a result of pointer motion to coordinates // rel_x,rel_y. client->x = rel_x; client->y = rel_y; movelim(client); } void mouseresize(struct client *client, int rel_x, int rel_y,bool accept_resize) { /* this solve the problem of over cpu usage while redrawing */ if (abs(rel_x - client->x) % MOVE_STEP_SLOW ==0 || abs(rel_y - client->y) %MOVE_STEP_SLOW == 0 || accept_resize) { client->width = abs(rel_x - client->x); client->height = abs(rel_y - client->y); client->width -= (client->width - client->base_width) % client->width_inc; client->height -= (client->height - client->base_height) % client->height_inc; PDEBUG("Trying to resize to %dx%d (%dx%d)\n", client->width, client->height, (client->width - client->base_width) / client->width_inc, (client->height - client->base_height) / client->height_inc); resizelim(client); if (client->vertmaxed) client->vertmaxed = false; if (client->hormaxed) client->hormaxed = false; } } void mouseresize_keepaspect(struct client *client, int rel_x, int rel_y) { //Not implemented yet, TODO later /* I'll do that later */ if (abs(rel_x -client->x) > abs(rel_x - client->x)) { client->height = client->height + client->height * (abs(rel_x - client->x)/(client->width)); client->width = abs(rel_x - client->x); } else { client->width = client->width + client->width * (abs(rel_y - client->y)/(client->height)); client->height= abs(rel_y - client->y); } client->width -= (client->width - client->base_width) % client->width_inc; client->height -= (client->height - client->base_height) % client->height_inc; PDEBUG("Trying to resize to %dx%d (%dx%d)\n", client->width, client->height, (client->width - client->base_width) / client->width_inc, (client->height - client->base_height) / client->height_inc); resizelim(client); if (client->vertmaxed) client->vertmaxed = false; if (client->hormaxed) client->hormaxed = false; } void movestep(struct client *client, char direction) { int16_t start_x; int16_t start_y; if (NULL == client) return; if (client->maxed) return; /* Save pointer position so we can warp pointer here later. */ if (!getpointer(client->id, &start_x, &start_y)) return; raisewindow(client->id); switch (direction) { case 'h': client->x = client->x - MOVE_STEP; break; case 'j': client->y = client->y + MOVE_STEP; break; case 'k': client->y = client->y - MOVE_STEP; break; case 'l': client->x = client->x + MOVE_STEP; break; default : PDEBUG("movestep: Moving in unknown direction.\n"); break; } /* switch direction */ movelim(client); /* If the pointer was inside the window to begin with, move * pointer back to where it was, relative to the window. */ if (start_x > 0 - conf.borderwidth && start_x < client->width + conf.borderwidth && start_y > 0 - conf.borderwidth && start_y < client->height + conf.borderwidth) { xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, start_x, start_y); xcb_flush(conn); } } void movestepslow(struct client *client, char direction) { int16_t start_x; int16_t start_y; if (NULL == client) return; if (client->maxed) return; /* Save pointer position so we can warp pointer here later. */ if (!getpointer(client->id, &start_x, &start_y)) return; raisewindow(client->id); switch (direction) { case 'h': client->x = client->x + MOVE_STEP_SLOW; break; case 'j': client->y = client->y - MOVE_STEP_SLOW; break; case 'k': client->y = client->y + MOVE_STEP_SLOW; break; case 'l': client->x = client->x - MOVE_STEP_SLOW; break; default : PDEBUG("movestepslow: Moving in unknown direction.\n"); break; } /* switch direction */ movelim(client); /* If the pointer was inside the window to begin with, move * pointer back to where it was, relative to the window. */ if (start_x > 0 - conf.borderwidth && start_x < client->width + conf.borderwidth && start_y > 0 - conf.borderwidth && start_y < client->height + conf.borderwidth) { xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, start_x, start_y); xcb_flush(conn); } } void setborders(struct client *client,bool isitfocused) { #ifdef DOUBLEBORDER /* this is the color maintainer */ uint32_t values[1]; uint32_t mask = 0; if (!client->maxed) { /* Set border width. */ values[0] = conf.borderwidth; mask = XCB_CONFIG_WINDOW_BORDER_WIDTH; xcb_configure_window(conn, client->id, mask, values); int half = conf.outer_border; xcb_rectangle_t rect_inner[] = { { client->width,0, conf.borderwidth-half,client->height+conf.borderwidth-half}, { client->width+conf.borderwidth+half,0, conf.borderwidth-half,client->height+conf.borderwidth-half}, { 0,client->height,client->width+conf.borderwidth-half,conf.borderwidth-half}, { 0, client->height+conf.borderwidth+half,client->width+conf.borderwidth-half,conf.borderwidth-half}, { client->width+conf.borderwidth+half,conf.borderwidth+client->height+half,conf.borderwidth,conf.borderwidth } }; xcb_rectangle_t rect_outer[] = { {client->width+conf.borderwidth-half,0,half,client->height+conf.borderwidth*2}, {client->width+conf.borderwidth,0,half,client->height+conf.borderwidth*2}, {0,client->height+conf.borderwidth-half,client->width+conf.borderwidth*2,half}, {0,client->height+conf.borderwidth,client->width+conf.borderwidth*2,half} }; xcb_pixmap_t pmap = xcb_generate_id(conn); /* my test have shown that drawing the pixmap directly on the * root window is faster then drawing it on the window directly */ xcb_create_pixmap(conn, screen->root_depth, pmap, screen->root, client->width+(conf.borderwidth*2), client->height+(conf.borderwidth*2)); xcb_gcontext_t gc = xcb_generate_id(conn); xcb_create_gc(conn, gc, pmap, 0, NULL); /* This is a fast test but a *single* state which would be uint_8_t would be better */ if (!client->unkillable && !client->fixed) { mask = XCB_GC_FOREGROUND; values[0] = conf.outer_border_col; xcb_change_gc(conn, gc, mask, &values[0]); xcb_poly_fill_rectangle(conn, pmap, gc, 4, rect_outer); } else { if (client->unkillable && client->fixed) { mask = XCB_GC_FOREGROUND; values[0] = conf.fixed_unkil_col; xcb_change_gc(conn, gc, mask, &values[0]); xcb_poly_fill_rectangle(conn, pmap, gc, 4, rect_outer); } else { if (client->fixed) { mask = XCB_GC_FOREGROUND; values[0] = conf.fixedcol; xcb_change_gc(conn, gc, mask, &values[0]); xcb_poly_fill_rectangle(conn, pmap, gc, 4, rect_outer); } else { mask = XCB_GC_FOREGROUND; values[0] = conf.unkillcol; xcb_change_gc(conn, gc, mask, &values[0]); xcb_poly_fill_rectangle(conn, pmap, gc, 4, rect_outer); } } } if (isitfocused) { mask = XCB_GC_FOREGROUND; values[0] = conf.focuscol; xcb_change_gc(conn, gc, mask, &values[0]); xcb_poly_fill_rectangle(conn, pmap, gc, 5, rect_inner); } else { mask = XCB_GC_FOREGROUND; values[0] = conf.unfocuscol; xcb_change_gc(conn, gc, mask, &values[0]); xcb_poly_fill_rectangle(conn, pmap, gc, 5, rect_inner); } values[0] = pmap; xcb_change_window_attributes(conn,client->id, XCB_CW_BORDER_PIXMAP, &values[0]); /* free the memory we allocated for the pixmap */ xcb_free_pixmap(conn,pmap); xcb_free_gc(conn,gc); xcb_flush(conn); } #else uint32_t values[1]; uint32_t mask = XCB_CW_BORDER_PIXEL; if (!client->maxed) { /* Set border width. */ values[0] = conf.borderwidth; mask = XCB_CONFIG_WINDOW_BORDER_WIDTH; xcb_configure_window(conn, client->id, mask, values); mask = XCB_CW_BORDER_PIXEL; if (isitfocused) { if (!client->unkillable && !client->fixed) { values[0] = conf.focuscol; xcb_change_window_attributes(conn,client->id, mask, &values[0]); } else { if (client->unkillable && client->fixed) { values[0] = conf.fixed_unkil_col; xcb_change_window_attributes(conn,client->id,mask,&values[0]); } else { if (client->fixed) { values[0] = conf.fixedcol; xcb_change_window_attributes(conn,client->id,mask, &values[0]); } else { values[0] = conf.unkillcol; xcb_change_window_attributes(conn,client->id,mask, &values[0]); } } } } else { values[0] = conf.unfocuscol; xcb_change_window_attributes(conn,client->id,mask,&values[0]); } } xcb_flush(conn); #endif } void unmax(struct client *client) { uint32_t values[5]; uint32_t mask = 0; if (NULL == client) { PDEBUG("unmax: client was NULL!\n"); return; } client->x = client->origsize.x; client->y = client->origsize.y; client->width = client->origsize.width; client->height = client->origsize.height; /* Restore geometry. */ if (client->maxed || client->hormaxed) { values[0] = client->x; values[1] = client->y; values[2] = client->width; values[3] = client->height; client->maxed = 0; client->hormaxed= 0; mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; } else { values[0] = client->y; values[1] = client->width; values[2] = client->height; mask = XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; } xcb_configure_window(conn, client->id, mask, values); /* Warp pointer to window or we might lose it. */ xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, client->width / 2, client->height / 2); setborders(client,true); xcb_flush(conn); } void maximize(struct client *client) { uint32_t values[4]; uint32_t mask = 0; int16_t mon_x; int16_t mon_y; uint16_t mon_width; uint16_t mon_height; if (NULL == client) { PDEBUG("maximize: client was NULL!\n"); return; } if (NULL == client->monitor) { mon_x = 0; mon_y = 0; mon_width = screen->width_in_pixels; mon_height = screen->height_in_pixels; } else { mon_x = client->monitor->x; mon_y = client->monitor->y; mon_width = client->monitor->width; mon_height = client->monitor->height; } /* Check if maximized already. If so, revert to stored geometry. */ if (client->maxed) { unmax(client); client->maxed = false; setborders(client,true); return; } /* Raise first. Pretty silly to maximize below something else. */ raisewindow(client->id); /* FIXME: Store original geom in property as well? */ client->origsize.x = client->x; client->origsize.y = client->y; client->origsize.width = client->width; client->origsize.height = client->height; /* Remove borders. */ values[0] = 0; mask = XCB_CONFIG_WINDOW_BORDER_WIDTH; xcb_configure_window(conn, client->id, mask, values); /* Move to top left and resize. */ client->x = mon_x+OFFSETX; client->y = mon_y+OFFSETY; client->width = mon_width-MAXWIDTH; client->height = mon_height-MAXHEIGHT; values[0] = client->x; values[1] = client->y; values[2] = client->width; values[3] = client->height; xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); xcb_flush(conn); client->maxed = true; } void maxvert(struct client *client) { uint32_t values[2]; int16_t mon_y; uint16_t mon_height; if (NULL == client) { PDEBUG("maxvert: client was NULL\n"); return; } if (NULL == client->monitor) { mon_y = 0; mon_height = screen->height_in_pixels; } else { mon_y = client->monitor->y; mon_height = client->monitor->height; } /* Check if maximized already. If so, revert to stored geometry. */ if (client->vertmaxed) { unmax(client); client->vertmaxed = false; setborders(client,true); return; } /* Raise first. Pretty silly to maximize below something else. */ raisewindow(client->id); /* Store original coordinates and geometry. * FIXME: Store in property as well? */ client->origsize.x = client->x; client->origsize.y = client->y; client->origsize.width = client->width; client->origsize.height = client->height; client->y = mon_y+OFFSETY; /* Compute new height considering height increments and screen height. */ client->height = mon_height - (conf.borderwidth * 2) - MAXHEIGHT; /* Move to top of screen and resize. */ values[0] = client->y; values[1] = client->height; xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_HEIGHT, values); xcb_flush(conn); /* Remember that this client is vertically maximized. */ client->vertmaxed = true; #ifdef DOUBLEBORDER setborders(client,true); #endif } void maxhor(struct client *client) { uint32_t values[2]; int16_t mon_x; uint16_t mon_width; if (NULL == client||client->maxed) { PDEBUG("maxhor: client was NULL\n"); return; } if (NULL == client->monitor) { mon_x = 0; mon_width = screen->width_in_pixels; } else { mon_x = client->monitor->x; mon_width = client->monitor->width; } /* Check if maximized already. If so, revert to stored geometry. */ if (client->hormaxed) { unmax(client); client->hormaxed = false; setborders(client,true); return; } /* Raise first. Pretty silly to maximize below something else. */ raisewindow(client->id); /* Store original coordinates and geometry. * FIXME: Store in property as well? */ client->origsize.x = client->x; client->origsize.y = client->y; client->origsize.width = client->width; client->origsize.height = client->height; client->x = mon_x+OFFSETX; /* Compute new height considering height increments and screen height. */ client->width = mon_width - (conf.borderwidth * 2) - MAXWIDTH; /* Move to top of screen and resize. */ values[0] = client->x; values[1] = client->width; xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_WIDTH, values); xcb_flush(conn); /* Remember that this client is vertically maximized. */ client->hormaxed = true; setborders(client,true); } void maxhorvert(struct client *client, bool top_down) { uint32_t values[4]; int16_t mon_x; int16_t mon_y; uint16_t mon_width; uint16_t mon_height; if (NULL == client||client->maxed) { PDEBUG("maximize: client was NULL!\n"); return; } if (NULL == client->monitor) { mon_x = 0; mon_y = 0; mon_width = screen->width_in_pixels; mon_height = screen->height_in_pixels; } else { mon_x = client->monitor->x; mon_y = client->monitor->y; mon_width = client->monitor->width; mon_height = client->monitor->height; } /* Check if maximized already. If so, revert to stored geometry. */ if (client->verthor) { unmax(client); client->verthor = false; setborders(client,true); return; } /* Raise first. Pretty silly to maximize below something else. */ raisewindow(client->id); /* FIXME: Store original geom in property as well? */ client->origsize.x = client->x; client->origsize.y = client->y; client->origsize.width = client->width; client->origsize.height = client->height; client->x = mon_x+OFFSETX; client->width = mon_width - (MAXWIDTH + (conf.borderwidth * 2)); client->height = ((float)(mon_height)/2)- (MAXHEIGHT+ (conf.borderwidth * 2)); if (top_down) client->y = mon_y+OFFSETY; else client->y = mon_y - OFFSETY + mon_height -(client->height + conf.borderwidth * 2); values[0] = client->width; values[1] = client->height; xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); xcb_flush(conn); movewindow(focuswin->id, client->x, client->y); client->verthor = true; #ifdef DOUBLEBORDER setborders(client,true); #endif } void maxverthor(struct client *client, bool right_left) { uint32_t values[4]; int16_t mon_x; int16_t mon_y; uint16_t mon_width; uint16_t mon_height; if (NULL == client||client->maxed) { PDEBUG("maximize: client was NULL!\n"); return; } if (NULL == client->monitor) { mon_x = 0; mon_y = 0; mon_width = screen->width_in_pixels; mon_height = screen->height_in_pixels; } else { mon_x = client->monitor->x; mon_y = client->monitor->y; mon_width = client->monitor->width; mon_height = client->monitor->height; } /* Check if maximized already. If so, revert to stored geometry. */ if (client->verthor) { unmax(client); client->verthor = false; setborders(client,true); return; } /* Raise first. Pretty silly to maximize below something else. */ raisewindow(client->id); /* FIXME: Store original geom in property as well? */ client->origsize.x = client->x; client->origsize.y = client->y; client->origsize.width = client->width; client->origsize.height = client->height; client->y = mon_y+OFFSETY; client->height = mon_height - (MAXHEIGHT + (conf.borderwidth * 2)); client->width = ((float)(mon_width)/2)- (MAXWIDTH+ (conf.borderwidth * 2)); if (right_left) client->x = mon_x+OFFSETX; else client->x = mon_x - OFFSETX + mon_width -(client->width + conf.borderwidth * 2); values[0] = client->width; values[1] = client->height; xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); xcb_flush(conn); movewindow(focuswin->id, client->x, client->y); client->verthor = true; #ifdef DOUBLEBORDER setborders(client,true); #endif } void hide(struct client *client) { long data[] = { XCB_ICCCM_WM_STATE_ICONIC, XCB_NONE }; /* Unmap window and declare iconic. * Unmapping will generate an UnmapNotify event so we can forget * about the window later. */ xcb_unmap_window(conn, client->id); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->id, wm_state, wm_state, 32, 2, data); xcb_flush(conn); } bool getpointer(xcb_drawable_t win, int16_t *x, int16_t *y) { xcb_query_pointer_reply_t *pointer; pointer = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win), 0); if (NULL == pointer) return false; *x = pointer->win_x; *y = pointer->win_y; free(pointer); return true; } bool getgeom(xcb_drawable_t win, int16_t *x, int16_t *y, uint16_t *width, uint16_t *height) { xcb_get_geometry_reply_t *geom; geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL); if (NULL == geom) return false; *x = geom->x; *y = geom->y; *width = geom->width; *height = geom->height; free(geom); return true; } void topleft(void) { int16_t pointx; int16_t pointy; int16_t mon_x; int16_t mon_y; if (NULL == focuswin|| NULL == wslist[curws]|| focuswin->maxed) return; if (NULL == focuswin->monitor) { mon_x = 0; mon_y = 0; } else { mon_x = focuswin->monitor->x; mon_y = focuswin->monitor->y; } raisewindow(focuswin->id); if (!getpointer(focuswin->id, &pointx, &pointy)) return; focuswin->x = mon_x; focuswin->y = mon_y; movewindow(focuswin->id, focuswin->x, focuswin->y); xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, pointx, pointy); xcb_flush(conn); } void topright() { int16_t pointx; int16_t pointy; int16_t mon_x; uint16_t mon_y; uint16_t mon_width; if (NULL == focuswin || NULL == wslist[curws] || focuswin->maxed) return; if (NULL == focuswin->monitor) { mon_width = screen->width_in_pixels + MAXWIDTH; mon_x = 0; mon_y = 0; } else { mon_width = focuswin->monitor->width + MAXWIDTH; mon_x = focuswin->monitor->x; mon_y = focuswin->monitor->y; } raisewindow(focuswin->id); if (!getpointer(focuswin->id, &pointx, &pointy)) return; focuswin->x = mon_x + mon_width - (focuswin->width + conf.borderwidth * 2); focuswin->y = mon_y; movewindow(focuswin->id, focuswin->x, focuswin->y); xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, pointx, pointy); xcb_flush(conn); } void botleft() { int16_t pointx; int16_t pointy; int16_t mon_x; int16_t mon_y; uint16_t mon_height; if (NULL == focuswin || focuswin->maxed) return; if (NULL == focuswin->monitor || NULL == wslist[curws]) { mon_x = 0; mon_y = 0; mon_height = screen->height_in_pixels + MAXHEIGHT; } else { mon_x = focuswin->monitor->x ; mon_y = focuswin->monitor->y; mon_height = focuswin->monitor->height +MAXHEIGHT; } raisewindow(focuswin->id); if (!getpointer(focuswin->id, &pointx, &pointy)) return; focuswin->x = mon_x; focuswin->y = mon_y + mon_height - (focuswin->height + conf.borderwidth * 2); movewindow(focuswin->id, focuswin->x, focuswin->y); xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, pointx, pointy); xcb_flush(conn); } void botright() { int16_t pointx; int16_t pointy; int16_t mon_x; int16_t mon_y; uint16_t mon_width; uint16_t mon_height; if (NULL == focuswin || NULL == wslist[curws] || focuswin->maxed) return; if (NULL == focuswin->monitor) { mon_x = 0; mon_y = 0; mon_width = screen->width_in_pixels + MAXWIDTH; mon_height = screen->height_in_pixels + MAXHEIGHT; } else { mon_x = focuswin->monitor->x; mon_y = focuswin->monitor->y; mon_width = focuswin->monitor->width + MAXWIDTH; mon_height = focuswin->monitor->height + MAXHEIGHT; } raisewindow(focuswin->id); if (!getpointer(focuswin->id, &pointx, &pointy)) return; focuswin->x = mon_x + mon_width - (focuswin->width + conf.borderwidth * 2); focuswin->y = mon_y + mon_height - (focuswin->height + conf.borderwidth * 2); movewindow(focuswin->id, focuswin->x, focuswin->y); xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, pointx, pointy); xcb_flush(conn); } void center() { int16_t pointx; int16_t pointy; int16_t mon_x; int16_t mon_y; uint16_t mon_height; uint16_t mon_width; if (NULL == focuswin|| NULL == wslist[curws]) return; if (focuswin->maxed) return; if (NULL == focuswin->monitor) { mon_x = 0; mon_y = 0; mon_height = screen->height_in_pixels + MAXHEIGHT; mon_width = screen->width_in_pixels + MAXWIDTH; } else { mon_x = focuswin->monitor->x; mon_y = focuswin->monitor->y; mon_height = focuswin->monitor->height + MAXHEIGHT; mon_width = focuswin->monitor->width + MAXWIDTH; } /* Save pointer position so we can warp pointer here later. */ if (!getpointer(focuswin->id, &pointx, &pointy)) return; raisewindow(focuswin->id); if (!getpointer(focuswin->id, &pointx, &pointy)) return; focuswin->x = mon_x; focuswin->x += mon_x + mon_width - (focuswin->width + conf.borderwidth * 2); focuswin->y = mon_y + mon_height - (focuswin->height + conf.borderwidth * 2); focuswin->y += mon_y; focuswin->y = focuswin->y /2; focuswin->x = focuswin->x /2; movewindow(focuswin->id, focuswin->x, focuswin->y); if (pointx > 0 - conf.borderwidth && pointx < focuswin->width + conf.borderwidth && pointy > 0 - conf.borderwidth && pointy < focuswin->height + conf.borderwidth) { xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, pointx, pointy); xcb_flush(conn); } } void deletewin(void) { xcb_get_property_cookie_t cookie; xcb_icccm_get_wm_protocols_reply_t protocols; bool use_delete = false; uint32_t i; if (NULL == focuswin || focuswin->unkillable==true) return; /* Check if WM_DELETE is supported. */ cookie = xcb_icccm_get_wm_protocols_unchecked(conn, focuswin->id, wm_protocols); if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &protocols, NULL) == 1) { for (i = 0; i < protocols.atoms_len; i++) { if (protocols.atoms[i] == wm_delete_window) { use_delete = true; } } } xcb_icccm_get_wm_protocols_reply_wipe(&protocols); if (use_delete) { xcb_client_message_event_t ev = { .response_type = XCB_CLIENT_MESSAGE, .format = 32, .sequence = 0, .window = focuswin->id, .type = wm_protocols, .data.data32 = { wm_delete_window, XCB_CURRENT_TIME } }; xcb_send_event(conn, false, focuswin->id, XCB_EVENT_MASK_NO_EVENT, (char *) &ev); } else xcb_kill_client(conn, focuswin->id); xcb_flush(conn); } void prevscreen(void) { struct item *item; if (NULL == focuswin || NULL == focuswin->monitor) return; item = focuswin->monitor->item->prev; if (NULL == item) return; float xpercentage = (float)(focuswin->x-focuswin->monitor->x)/(focuswin->monitor->width); float ypercentage = (float)(focuswin->y-focuswin->monitor->y)/(focuswin->monitor->height); focuswin->monitor = item->data; raisewindow(focuswin->id); focuswin->x = focuswin->monitor->width * xpercentage + focuswin->monitor->x; focuswin->y = focuswin->monitor->height * ypercentage + focuswin->monitor->y; fitonscreen(focuswin); movelim(focuswin); xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, 0, 0); xcb_flush(conn); } void nextscreen(void) { struct item *item; if (NULL == focuswin || NULL == focuswin->monitor) return; item = focuswin->monitor->item->next; if (NULL == item) return; float xpercentage = (float)(focuswin->x-focuswin->monitor->x)/(focuswin->monitor->width); float ypercentage = (float)(focuswin->y-focuswin->monitor->y)/(focuswin->monitor->height); focuswin->monitor = item->data; raisewindow(focuswin->id); focuswin->x = focuswin->monitor->width * xpercentage + focuswin->monitor->x; focuswin->y = focuswin->monitor->height * ypercentage + focuswin->monitor->y; fitonscreen(focuswin); movelim(focuswin); xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, 0, 0); xcb_flush(conn); } void cursor_move(uint8_t direction, bool fast) { // Function to make the cursor move with the keyboard switch (direction) { case 0: if (fast) xcb_warp_pointer(conn, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, -MOUSE_MOVE_FAST); else xcb_warp_pointer(conn, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, -MOUSE_MOVE_SLOW); break; case 1: if (fast) xcb_warp_pointer(conn, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, MOUSE_MOVE_FAST); else xcb_warp_pointer(conn, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, MOUSE_MOVE_SLOW); break; case 2: if (fast) xcb_warp_pointer(conn, XCB_NONE, XCB_NONE, 0, 0, 0, 0, MOUSE_MOVE_FAST, 0); else xcb_warp_pointer(conn, XCB_NONE, XCB_NONE, 0, 0, 0, 0, MOUSE_MOVE_SLOW, 0); break; case 3: if (fast) xcb_warp_pointer(conn, XCB_NONE, XCB_NONE, 0, 0, 0, 0, -MOUSE_MOVE_FAST, 0); else xcb_warp_pointer(conn, XCB_NONE, XCB_NONE, 0, 0, 0, 0, -MOUSE_MOVE_SLOW, 0); break; default : break; } xcb_flush(conn); } void handle_keypress(xcb_key_press_event_t *ev) { int i; key_enum_t key; for (key = KEY_MAX, i = KEY_F; i < KEY_MAX; i ++) { if (ev->detail == keys[i].keycode && 0 != keys[i].keycode) { key = i; break; } } if (key == KEY_MAX) { PDEBUG("Unknown key pressed.\n"); /* We don't know what to do with this key. Send this key press * event to the focused window. */ xcb_send_event(conn, false, XCB_SEND_EVENT_DEST_ITEM_FOCUS, XCB_EVENT_MASK_NO_EVENT, (char *) ev); xcb_flush(conn); return; } if (MCWM_TABBING == mode && key != KEY_TAB && key != KEY_BACKTAB) /* First finish tabbing around. Then deal with the next key. */ finishtabbing(); /* Is it both shifted and controlmasked ? */ if ((ev->state & SHIFTMOD) && (ev->state & CONTROLMOD)) { switch (key) { case KEY_H: /* h */ resizestepslow(focuswin, 'h'); break; case KEY_J: /* j */ resizestepslow(focuswin, 'j'); break; case KEY_K: /* k */ resizestepslow(focuswin, 'k'); break; case KEY_L: /* l */ resizestepslow(focuswin, 'l'); break; default : break; } } /* This is suppose to resize keeping aspect ratio */ else if (ev->state & ALTMOD && ALTMOD!=-1) { switch (key) { case KEY_GROW: /* h */ resizestep_keep_aspect(focuswin, 'l'); break; case KEY_SHRINK: /* l */ resizestep_keep_aspect(focuswin, 'h'); break; default : break; } } /* control mask? */ else if (ev->state & CONTROLMOD) { switch (key) { case KEY_END: mcwm_exit(); break; case KEY_R: mcwm_restart(); break; case KEY_H: /* h */ movestepslow(focuswin, 'l'); break; case KEY_J: /* j */ movestepslow(focuswin, 'k'); break; case KEY_K: /* k */ movestepslow(focuswin, 'j'); break; case KEY_L: /* l */ movestepslow(focuswin, 'h'); break; default : break; } } /* Is it shifted? */ else if (ev->state & SHIFTMOD) { switch (key) { case KEY_H: /* h */ resizestep(focuswin, 'h'); break; case KEY_J: /* j */ resizestep(focuswin, 'j'); break; case KEY_K: /* k */ resizestep(focuswin, 'k'); break; case KEY_L: /* l */ resizestep(focuswin, 'l'); break; case KEY_TAB: /* shifted tab counts as backtab */ focusnext(true); break; case KEY_1: sendtoworkspace(focuswin, 0); break; case KEY_2: sendtoworkspace(focuswin, 1); break; case KEY_3: sendtoworkspace(focuswin, 2); break; case KEY_4: sendtoworkspace(focuswin, 3); break; case KEY_5: sendtoworkspace(focuswin, 4); break; case KEY_6: sendtoworkspace(focuswin, 5); break; case KEY_7: sendtoworkspace(focuswin, 6); break; case KEY_8: sendtoworkspace(focuswin, 7); break; case KEY_9: sendtoworkspace(focuswin, 8); break; case KEY_0: sendtoworkspace(focuswin, 9); break; case KEY_U: maxverthor(focuswin,false); break; case KEY_Y: maxverthor(focuswin,true); break; case KEY_B: maxhorvert(focuswin,false); break; case KEY_N: maxhorvert(focuswin,true); break; case KEY_M: maxhor(focuswin); break; case KEY_UP: cursor_move(0, true); break; case KEY_DOWN: cursor_move(1, true); break; case KEY_RIGHT: cursor_move(2, true); break; case KEY_LEFT: cursor_move(3, true); break; default : /* Ignore other shifted keys. */ break; } } else { switch (key) { case KEY_RET: /* return */ start(conf.terminal); break; case KEY_P: /* P */ start(conf.menu); break; case KEY_F: /* f */ fixwindow(focuswin); break; case KEY_H: /* h */ movestep(focuswin, 'h'); break; case KEY_J: /* j */ movestep(focuswin, 'j'); break; case KEY_K: /* k */ movestep(focuswin, 'k'); break; case KEY_L: /* l */ movestep(focuswin, 'l'); break; case KEY_TAB: /* tab */ focusnext(false); break; case KEY_BACKTAB: /* backtab */ focusnext(true); break; case KEY_M: /* m */ maxvert(focuswin); break; case KEY_R: /* r*/ raiseorlower(focuswin); break; case KEY_X: /* x */ maximize(focuswin); break; case KEY_1: changeworkspace(0); break; case KEY_2: changeworkspace(1); break; case KEY_3: changeworkspace(2); break; case KEY_4: changeworkspace(3); break; case KEY_5: changeworkspace(4); break; case KEY_6: changeworkspace(5); break; case KEY_7: changeworkspace(6); break; case KEY_8: changeworkspace(7); break; case KEY_9: changeworkspace(8); break; case KEY_0: changeworkspace(9); break; case KEY_Y: topleft(); break; case KEY_U: topright(); break; case KEY_B: botleft(); break; case KEY_N: botright(); break; case KEY_G: center(); break; case KEY_END: deletewin(); break; case KEY_PREVSCR: prevscreen(); break; case KEY_NEXTSCR: nextscreen(); break; case KEY_ICONIFY: if (conf.allowicons) { hide(focuswin); } break; case KEY_PREVWS: if (curws >0) { changeworkspace(curws -1); } else { changeworkspace(WORKSPACES -1); } break; case KEY_NEXTWS: changeworkspace((curws +1) % WORKSPACES); break; case KEY_GROW: if (ALTMOD==-1) resizestep_keep_aspect(focuswin, 'l'); break; case KEY_SHRINK: if (ALTMOD==-1) resizestep_keep_aspect(focuswin, 'h'); break; case KEY_UNKILLABLE: unkillablewindow(focuswin); break; case KEY_UP: cursor_move(0, false); break; case KEY_DOWN: cursor_move(1, false); break; case KEY_RIGHT: cursor_move(2, false); break; case KEY_LEFT: cursor_move(3, false); break; default : /* Ignore other keys. */ break; } /* switch unshifted */ } } /* handle_keypress() */ void configwin(xcb_window_t win, uint16_t mask, struct winconf wc) { // Helper function to configure a window. uint32_t values[7]; int i = -1; if (mask & XCB_CONFIG_WINDOW_X) { mask |= XCB_CONFIG_WINDOW_X; i ++; values[i] = wc.x; } if (mask & XCB_CONFIG_WINDOW_Y) { mask |= XCB_CONFIG_WINDOW_Y; i ++; values[i] = wc.y; } if (mask & XCB_CONFIG_WINDOW_WIDTH) { mask |= XCB_CONFIG_WINDOW_WIDTH; i ++; values[i] = wc.width; } if (mask & XCB_CONFIG_WINDOW_HEIGHT) { mask |= XCB_CONFIG_WINDOW_HEIGHT; i ++; values[i] = wc.height; } if (mask & XCB_CONFIG_WINDOW_SIBLING) { mask |= XCB_CONFIG_WINDOW_SIBLING; i ++; values[i] = wc.sibling; } if (mask & XCB_CONFIG_WINDOW_STACK_MODE) { mask |= XCB_CONFIG_WINDOW_STACK_MODE; i ++; values[i] = wc.stackmode; } if (-1 != i) { xcb_configure_window(conn, win, mask, values); xcb_flush(conn); } } void configurerequest(xcb_configure_request_event_t *e) { struct client *client; struct winconf wc; int16_t mon_x; int16_t mon_y; uint16_t mon_width; uint16_t mon_height; PDEBUG("event: Configure request. mask = %d\n", e->value_mask); /* Find the client. */ if ((client = findclient(e->window))) { /* Find monitor position and size. */ if (NULL == client || NULL == client->monitor) { mon_x = 0; mon_y = 0; mon_width = screen->width_in_pixels; mon_height = screen->height_in_pixels; } else { mon_x = client->monitor->x; mon_y = client->monitor->y; mon_width = client->monitor->width; mon_height = client->monitor->height; } if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) { /* Don't resize if maximized. */ if (!client->maxed && !client->hormaxed) client->width = e->width; } if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) { /* Don't resize if maximized. */ if (!client->maxed && !client->vertmaxed) client->height = e->height; } /* XXX Do we really need to pass on sibling and stack mode * configuration? Do we want to? */ if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) { uint32_t values[1]; values[0] = e->sibling; xcb_configure_window(conn, e->window, XCB_CONFIG_WINDOW_SIBLING, values); xcb_flush(conn); } if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) { uint32_t values[1]; values[0] = e->stack_mode; xcb_configure_window(conn, e->window, XCB_CONFIG_WINDOW_STACK_MODE, values); xcb_flush(conn); } /* Check if window fits on screen after resizing. */ if (!client->maxed) { /* Check if window fits on screen after resizing. */ if (client->x + client->width + 2 * conf.borderwidth > mon_x + mon_width) { /* See if it fits if we move away the window from the * right edge of the screen. */ client->x = mon_x + mon_width - (client->width + 2 * conf.borderwidth); /* If we moved over the left screen edge, move back and * fit exactly on screen. */ if (client->x < mon_x) { client->x = mon_x; client->width = mon_width - 2 * conf.borderwidth; } } if (client->y + client->height + 2 * conf.borderwidth > mon_y + mon_height) { /* See if it fits if we move away the window from the * bottom edge. */ client->y = mon_y + mon_height - (client->height + 2 * conf.borderwidth); /* If we moved over the top screen edge, move back and fit * on screen. */ if (client->y < mon_y) { PDEBUG("over the edge: y < %d\n", mon_y); client->y = mon_y; client->height = mon_height - 2 * conf.borderwidth; } } moveresize(client->id, client->x, client->y, client->width, client->height); } setborders(client,true); } else { PDEBUG("We don't know about this window yet.\n"); /* Unmapped window. Just pass all options except border width. */ wc.x = e->x; wc.y = e->y; wc.width = e->width; wc.height = e->height; wc.sibling = e->sibling; wc.stackmode = e->stack_mode; configwin(e->window, e->value_mask, wc); } } void events(void) { xcb_generic_event_t *ev; int16_t mode_x = 0; /* X coord when in special mode */ int16_t mode_y = 0; /* Y coord when in special mode */ int fd; /* Our X file descriptor */ fd_set in; /* For select */ int found; /* Ditto. */ /* Get the file descriptor so we can do select() on it. */ fd = xcb_get_file_descriptor(conn); for (sigcode = 0; 0 == sigcode;) { /* Prepare for select(). */ FD_ZERO(&in); FD_SET(fd, &in); /* Check for events, again and again. When poll returns NULL * (and it does that a lot), we block on select() until the * event file descriptor gets readable again. * We do it this way instead of xcb_wait_for_event() since * select() will return if we were interrupted by a signal. We * like that. */ ev = xcb_poll_for_event(conn); if (NULL == ev) { PDEBUG("xcb_poll_for_event() returned NULL.\n"); /* * Check if we have an unrecoverable connection error, * like a disconnected X server. */ if (xcb_connection_has_error(conn)) { cleanup(0); exit(1); } found = select(fd + 1, &in, NULL, NULL, NULL); if (-1 == found) { if (EINTR == errno) /* We received a signal. Break out of loop. */ break; else { /* Something was seriously wrong with select(). */ fprintf(stderr, "mcwm: select failed."); cleanup(0); exit(1); } } else /* We found more events. Goto start of loop. */ continue; } #ifdef DEBUG if (ev->response_type <= MAXEVENTS) PDEBUG("Event: %s\n", evnames[ev->response_type]); else PDEBUG("Event: #%d. Not known.\n", ev->response_type); #endif /* Note that we ignore XCB_RANDR_NOTIFY. */ if (ev->response_type == randrbase + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { PDEBUG("RANDR screen change notify. Checking outputs.\n"); getrandr(); free(ev); continue; } switch (ev->response_type & ~0x80) { case XCB_EVENT_MASK_BUTTON_PRESS: { xcb_button_press_event_t *e = (xcb_button_press_event_t *)ev; PDEBUG("Button %d pressed in window %ld, subwindow %d " "coordinates (%d,%d)\n", e->detail, (long)e->event, e->child, e->event_x, e->event_y); if (0 == e->child) { switch (e->detail) { case XCB_BUTTON_INDEX_1: /* Mouse button one. */ start(MOUSE1); break; case XCB_BUTTON_INDEX_2: /* Middle mouse button. */ start(MOUSE2); break; case XCB_BUTTON_INDEX_3: /* Mouse button three. */ start(MOUSE3); break; default : break; } /* switch */ /* Break out of event switch. */ break; } /* If we don't have any currently focused window, we can't * do anything. We don't want to do anything if the mouse * cursor is in the wrong window (root window or a panel, * for instance). There is a limit to sloppy focus. */ if (NULL == focuswin || focuswin->id != e->child) break; /* If middle button was pressed, raise window or lower * it if it was already on top. */ if (2 == e->detail) raiseorlower(focuswin); else { int16_t pointx; int16_t pointy; /* We're moving or resizing. */ /* Get and save pointer position inside the window * so we can go back to it when we're done moving * or resizing. */ if (!getpointer(focuswin->id, &pointx, &pointy)) break; mode_x = pointx; mode_y = pointy; /* Raise window. */ raisewindow(focuswin->id); /* Mouse button 1 was pressed. */ if (1 == e->detail) { mode = MCWM_MOVE; /* Warp pointer to upper left of window before * starting move. */ xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, 1, 1); } else { /* Mouse button 3 was pressed. */ mode = MCWM_RESIZE; /* Warp pointer to lower right. */ xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, focuswin->width, focuswin->height); //Attempt for the ghostly move //MCWM_RESIZE mode_x = pointx; mode_y = pointy; } /* Take control of the pointer in the root window * and confine it to root. * Give us events when the key is released or if * any motion occurs with the key held down. * Keep updating everything else. * Don't use any new cursor. */ xcb_grab_pointer(conn, 0, screen->root, XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root, XCB_NONE, XCB_CURRENT_TIME); xcb_flush(conn); PDEBUG("mode now : %d\n", mode); } } break; case XCB_MAP_REQUEST: { xcb_map_request_event_t *e; PDEBUG("event: Map request.\n"); e = (xcb_map_request_event_t *) ev; newwin(e->window); } break; case XCB_DESTROY_NOTIFY: { xcb_destroy_notify_event_t *e; e = (xcb_destroy_notify_event_t *) ev; /* If we had focus or our last focus in this window, * forget about the focus. * We will get an EnterNotify if there's another window * under the pointer so we can set the focus proper later. */ if (NULL != focuswin) { if (focuswin->id == e->window) focuswin = NULL; } if (NULL != lastfocuswin) { if (lastfocuswin->id == e->window) lastfocuswin = NULL; } /* Find this window in list of clients and forget about it. */ forgetwin(e->window); } break; case XCB_MOTION_NOTIFY: { xcb_query_pointer_reply_t *pointer; /* We can't do anything if we don't have a focused window * or if it's fully maximized. */ if (NULL == focuswin || focuswin->maxed) break; /* This is not really a real notify, but just a hint that * the mouse pointer moved. This means we need to get the * current pointer position ourselves. */ pointer = xcb_query_pointer_reply( conn, xcb_query_pointer(conn, screen->root), 0); if (NULL == pointer) { PDEBUG("Couldn't get pointer position.\n"); break; } /* Our pointer is moving and since we even get this event * we're either resizing or moving a window. */ if (mode == MCWM_MOVE) mousemove(focuswin, pointer->root_x, pointer->root_y); else if (mode == MCWM_RESIZE) { /*I love spooky resize, it only resize when you release *the mouse button which reduce considerably the cpu overussage *I'll try to include the resize from each border here *we'll need to calculate the difference between the mouse *position and the corners position *Thus we need the corner positions *roughly (may be wrong, I need to check that): *(client->x,client->y) *(client->x+client->width,client->y) *(client->x,client->y+client->height) *(client->x,client->width,client->y+client->height) *To know which point is the closest we do this *we take the lowest absolute difference between the coordinates *of the corners and the pointer */ #ifndef SPOOKY_RESIZE mouseresize(focuswin, pointer->root_x, pointer->root_y,false); #endif } else PDEBUG("Motion event when we're not moving our resizing!\n"); free(pointer); } break; case XCB_BUTTON_RELEASE: { PDEBUG("Mouse button released! mode = %d\n", mode); if (0 == mode) /* Mouse button released, but not in a saved mode. Do nothing. */ break; else { if (mode==MCWM_RESIZE) { xcb_query_pointer_reply_t *pointer; pointer = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, screen->root), 0); mouseresize(focuswin, pointer->root_x, pointer->root_y,true); free(pointer); } /* We're finished moving or resizing. */ if (NULL == focuswin) { /* We don't seem to have a focused window! Just * ungrab and reset the mode. */ PDEBUG("No focused window when finished moving or " "resizing!"); xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); xcb_flush(conn); /* Important! */ mode = 0; break; } /* We will get an EnterNotify and focus another window * if the pointer just happens to be on top of another * window when we ungrab the pointer, so we have to * warp the pointer before to prevent this. * Move to saved position within window or if that * position is now outside current window, move inside * window. * what happens here is that we release the mouse button * and put the mode back to 0 (which is not MCWM_RESIZE nor MCWM_MOVE) * This tag is stupid and only here to help me browser the code. * MCWM_RESIZE; * commented that because it gets annoying to always have the pointer jump around */ /* if (mode_x > focuswin->width) { x = focuswin->width / 2; if (0 == x) { x = 1; } } else { x = mode_x; } if (mode_y > focuswin->height) { y = focuswin->height / 2; if (0 == y) { y = 1; } } else { y = mode_y; } */ /*xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, x, y);*/ xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); xcb_flush(conn); /* Important! */ mode = 0; PDEBUG("mode now = %d\n", mode); } } break; case XCB_KEY_PRESS: { xcb_key_press_event_t *e = (xcb_key_press_event_t *)ev; PDEBUG("Key %d pressed\n", e->detail); handle_keypress(e); } break; case XCB_KEY_RELEASE: { xcb_key_release_event_t *e = (xcb_key_release_event_t *)ev; unsigned i; PDEBUG("Key %d released.\n", e->detail); if (MCWM_TABBING == mode) { /* Check if it's the that was released was a key * generating the MODKEY mask. */ for (i = 0; i < modkeys.len; i ++) { PDEBUG("Is it %d?\n", modkeys.keycodes[i]); if (e->detail == modkeys.keycodes[i]) { finishtabbing(); /* Get out of for... */ break; } } /* for keycodes. */ } /* if tabbing. */ } break; case XCB_ENTER_NOTIFY: { xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *)ev; struct client *client; PDEBUG("event: Enter notify eventwin %d, child %d, detail %d.\n", e->event, e->child, e->detail); /* If this isn't a normal enter notify, don't bother. * We also need ungrab events, since these will be * generated on button and key grabs and if the user for * some reason presses a button on the root and then moves * the pointer to our window and releases the button, we * get an Ungrab EnterNotify. * The other cases means the pointer is grabbed and that * either means someone is using it for menu selections or * that we're moving or resizing. We don't want to change * focus in those cases. */ if (e->mode == XCB_NOTIFY_MODE_NORMAL || e->mode == XCB_NOTIFY_MODE_UNGRAB) { /* If we're entering the same window we focus now, * then don't bother focusing. */ if (NULL == focuswin || e->event != focuswin->id) { /* Otherwise, set focus to the window we just * entered if we can find it among the windows we * know about. If not, just keep focus in the old * window. */ client = findclient(e->event); if (NULL != client) { if (MCWM_TABBING != mode) { /* We are focusing on a new window. Since * we're not currently tabbing around the * window ring, we need to update the * current workspace window list: Move * first the old focus to the head of the * list and then the new focus to the head * of the list. */ if (NULL != focuswin) { movetohead(&wslist[curws], focuswin->wsitem[curws]); lastfocuswin = NULL; } movetohead(&wslist[curws], client->wsitem[curws]); } /* if not tabbing */ setfocus(client); setborders(client,true); } } } } break; case XCB_CONFIGURE_NOTIFY: { xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *)ev; if (e->window == screen->root) { /* When using RANDR or Xinerama, the root can change * geometry when the user adds a new screen, tilts * their screen 90 degrees or whatnot. We might need * to rearrange windows to be visible. * We might get notified for several reasons, not just * if the geometry changed. If the geometry is * unchanged we do nothing. */ PDEBUG("Notify event for root!\n"); PDEBUG("Possibly a new root geometry: %dx%d\n", e->width, e->height); if (e->width == screen->width_in_pixels && e->height == screen->height_in_pixels) { /* Root geometry is really unchanged. Do nothing. */ PDEBUG("Hey! Geometry didn't change.\n"); } else { screen->width_in_pixels = e->width; screen->height_in_pixels = e->height; /* Check for RANDR. */ if (-1 == randrbase) { /* We have no RANDR so we rearrange windows to * the new root geometry here. * With RANDR enabled, we handle this per * screen getrandr() when we receive an * XCB_RANDR_SCREEN_CHANGE_NOTIFY event. */ arrangewindows(); } } } } break; case XCB_CONFIGURE_REQUEST: configurerequest((xcb_configure_request_event_t *) ev); break; case XCB_CLIENT_MESSAGE: { xcb_client_message_event_t *e = (xcb_client_message_event_t *)ev; if (conf.allowicons) { if (e->type == wm_change_state && e->format == 32 && e->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC) { long data[] = { XCB_ICCCM_WM_STATE_ICONIC, XCB_NONE }; /* Unmap window and declare iconic. */ xcb_unmap_window(conn, e->window); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, e->window, wm_state, wm_state, 32, 2, data); xcb_flush(conn); } } /* if */ } break; case XCB_CIRCULATE_REQUEST: { xcb_circulate_request_event_t *e = (xcb_circulate_request_event_t *)ev; /* Subwindow e->window to parent e->event is about to be * restacked. * Just do what was requested, e->place is either * XCB_PLACE_ON_TOP or _ON_BOTTOM. We don't care. */ xcb_circulate_window(conn, e->window, e->place); } break; case XCB_MAPPING_NOTIFY: { xcb_mapping_notify_event_t *e = (xcb_mapping_notify_event_t *)ev; /* XXX Gah! We get a new notify message for *every* key! * We want to know when the entire keyboard is finished. * Impossible? Better handling somehow?*/ /* We're only interested in keys and modifiers, not * pointer mappings, for instance. */ if (e->request != XCB_MAPPING_MODIFIER && e->request != XCB_MAPPING_KEYBOARD) break; /* Forget old key bindings. */ xcb_ungrab_key(conn, XCB_GRAB_ANY, screen->root, XCB_MOD_MASK_ANY); /* Use the new ones. */ setupkeys(); } break; case XCB_UNMAP_NOTIFY: { xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *)ev; struct item *item; struct client *client; /* Find the window in our *current* workspace list, then * forget about it. If it gets mapped, we add it to our * lists again then. * Note that we might not know about the window we got the * UnmapNotify event for. It might be a window we just * unmapped on *another* workspace when changing * workspaces, for instance, or it might be a window with * override redirect set. This is not an error. * XXX We might need to look in the global window list, * after all. Consider if a window is unmapped on our last * workspace while changing workspaces... If we do this, * we need to keep track of our own windows and ignore * UnmapNotify on them. */ for (item = wslist[curws]; item != NULL; item = item->next) { client = item->data; if (client->id == e->window) { PDEBUG("Forgetting about %d\n", e->window); if (focuswin == client) focuswin = NULL; forgetclient(client); /* We're finished. Break out of for loop. */ break; } } /* for */ } break; } /* switch */ /* Forget about this event. */ free(ev); } } void printhelp(void) { printf("mcwm: Usage: mcwm [-b] [-t terminal-program] [-m menu-program] [-f colour] " "[-u colour] [-x colour] \n"); printf(" -b means draw no borders\n"); printf(" -t urxvt will start urxvt when MODKEY + Return is pressed\n"); printf(" -m mcwm will start a menu when MODKEY + P is pressed\n"); printf(" -f colour sets colour for focused window borders of focused " "to a named color.\n"); printf(" -u colour sets colour for unfocused window borders."); printf(" -x color sets colour for fixed window borders.\n"); printf(" -k color sets colour for unkillable window borders.\n"); printf("Mcwm patched by Beastie, enjoy ;)\n"); } void sigcatch(const int sig) { sigcode = sig; } xcb_atom_t getatom(char *atom_name) // Get a defined atom from the X server. { xcb_intern_atom_cookie_t atom_cookie; xcb_atom_t atom; xcb_intern_atom_reply_t *rep; atom_cookie = xcb_intern_atom(conn, 0, strlen(atom_name), atom_name); rep = xcb_intern_atom_reply(conn, atom_cookie, NULL); if (NULL != rep) { atom = rep->atom; free(rep); return atom; } /* XXX Note that we return 0 as an atom if anything goes wrong. * Might become interesting.*/ return 0; } void mcwm_restart(void) //restart mcwm and exit mcwm { // Problems: I want exit to kill the X session, xinit, main terminal //so that we quit X at the same time. xcb_set_input_focus(conn, XCB_NONE, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_CURRENT_TIME); xcb_flush(conn); execvp("/usr/local/bin/mcwm", user_argv); } void mcwm_exit(void) { cleanup(0); exit(0); } int main(int argc, char **argv) { user_argv = argv; uint32_t mask = 0; uint32_t values[2]; int ch; /* Option character */ xcb_void_cookie_t cookie; xcb_generic_error_t *error; xcb_drawable_t root; char *focuscol; char *unfocuscol; char *fixedcol; char *unkillcol; char *empty_col; char *fixed_unkil_col; int scrno; xcb_screen_iterator_t iter; /* Install signal handlers. */ /* We ignore child exists. Don't create zombies. */ if (SIG_ERR == signal(SIGCHLD, SIG_IGN)) { perror("mcwm: signal"); exit(1); } if (SIG_ERR == signal(SIGINT, sigcatch)) { perror("mcwm: signal"); exit(1); } if (SIG_ERR == signal(SIGTERM, sigcatch)) { perror("mcwm: signal"); exit(1); } /* Set up defaults. */ conf.borderwidth = BORDERWIDTH; #ifdef DOUBLEBORDER conf.outer_border= OUTER_BORDER; #endif conf.terminal = TERMINAL; conf.menu = MENU; conf.allowicons = ALLOWICONS; focuscol = FOCUSCOL; unfocuscol = UNFOCUSCOL; fixedcol = FIXEDCOL; unkillcol = UNKILLCOL; fixed_unkil_col = FIXED_UNKIL_COL; empty_col = EMPTY_COL; while (1) { ch = getopt(argc, argv, "b:it:m:f:u:x:k:"); if (-1 == ch) /* No more options, break out of while loop. */ break; switch (ch) { case 'b': /* Border width */ conf.borderwidth = atoi(optarg); break; case 'i': conf.allowicons = true; break; case 't': conf.terminal = optarg; break; case 'm': conf.menu = optarg; break; case 'f': focuscol = optarg; break; case 'u': unfocuscol = optarg; break; case 'x': fixedcol = optarg; break; case 'k': unkillcol = optarg; break; case 'K': fixed_unkil_col = optarg; default : printhelp(); exit(0); } /* switch */ } /* Use $DISPLAY. After connecting scrno will contain the value of * the display's screen. */ conn = xcb_connect(NULL, &scrno); if (xcb_connection_has_error(conn)) { perror("xcb_connect"); exit(1); } /* Find our screen. */ iter = xcb_setup_roots_iterator(xcb_get_setup(conn)); for (int i = 0; i < scrno; ++ i) xcb_screen_next(&iter); screen = iter.data; if (!screen) { fprintf(stderr, "mcwm: Can't get the current screen. Exiting.\n"); xcb_disconnect(conn); exit(1); } root = screen->root; PDEBUG("Screen size: %dx%d\nRoot window: %d\n", screen->width_in_pixels, screen->height_in_pixels, screen->root); /* Get some colours. */ conf.focuscol = getcolor(focuscol); conf.unfocuscol = getcolor(unfocuscol); conf.fixedcol = getcolor(fixedcol); conf.unkillcol = getcolor(unkillcol); conf.outer_border_col= getcolor(OUTER_BORDER_COL); if (strcmp(empty_col,"0")==0) conf.empty_col = 0; else conf.empty_col = getcolor(empty_col); conf.fixed_unkil_col = getcolor(fixed_unkil_col); /* Get some atoms. */ atom_desktop = getatom("_NET_WM_DESKTOP"); atom_current_desktop = getatom("_NET_CURRENT_DESKTOP"); atom_unkillable = getatom("_NET_UNKILLABLE"); wm_delete_window = getatom("WM_DELETE_WINDOW"); wm_change_state = getatom("WM_CHANGE_STATE"); wm_state = getatom("WM_STATE"); wm_protocols = getatom("WM_PROTOCOLS"); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, screen->root, atom_current_desktop, XCB_ATOM_CARDINAL, 32, 1,&curws); /* Check for RANDR extension and configure. */ randrbase = setuprandr(); /* Loop over all clients and set up stuff. */ if (0 != setupscreen()) { fprintf(stderr, "mcwm: Failed to initialize windows. Exiting.\n"); xcb_disconnect(conn); exit(1); } /* Set up key bindings. */ if (0 != setupkeys()) { fprintf(stderr, "mcwm: Couldn't set up keycodes. Exiting."); xcb_disconnect(conn); exit(1); } /* Grab mouse buttons. */ xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 1 /* left mouse button */, MOUSEMODKEY); xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 2 /* middle mouse button */, MOUSEMODKEY); xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 3 /* right mouse button */, MOUSEMODKEY); /* This fixes the numlock issue */ xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 1 /* left mouse button */, XCB_MOD_MASK_2| MOUSEMODKEY); xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 2 /* middle mouse button */, XCB_MOD_MASK_2| MOUSEMODKEY); xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 3 /* right mouse button */, XCB_MOD_MASK_2| MOUSEMODKEY); /* Subscribe to events. */ mask = XCB_CW_EVENT_MASK; values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; cookie = xcb_change_window_attributes_checked(conn, root, mask, values); error = xcb_request_check(conn, cookie); xcb_flush(conn); if (NULL != error) { fprintf(stderr, "mcwm: Can't get SUBSTRUCTURE REDIRECT. " "Error code: %d\n" "Another window manager running? Exiting.\n", error->error_code); xcb_disconnect(conn); exit(1); } /* Loop over events. */ events(); /* Die gracefully. */ cleanup(sigcode); exit(0); }