diff options
Diffstat (limited to 'mcwm.c')
-rw-r--r-- | mcwm.c | 1954 |
1 files changed, 1296 insertions, 658 deletions
@@ -38,6 +38,7 @@ #include <sys/select.h> #include <xcb/xcb.h> +#include <xcb/randr.h> #include <xcb/xcb_keysyms.h> #include <xcb/xcb_atom.h> #include <xcb/xcb_icccm.h> @@ -81,8 +82,8 @@ */ #define MCWM_TABBING 4 -/* Our highest workspace. */ -#define WORKSPACE_MAX 9 +/* Number of workspaces. */ +#define WORKSPACES 10 /* Value in WM hint which means this window is fixed on all workspaces. */ #define NET_WM_FIXED 0xffffffff @@ -120,18 +121,41 @@ typedef enum { KEY_B, KEY_N, KEY_END, + KEY_PREVSCR, + KEY_NEXTSCR, 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; +}; + /* Everything we know about a window. */ struct client { xcb_drawable_t id; /* ID of this window. */ bool usercoord; /* X,Y was set by -geom. */ - uint32_t x; /* X coordinate. Only updated when maxed. */ - uint32_t y; /* Y coordinate. Ditto. */ - uint16_t width; /* Width in pixels. Ditto. */ - uint16_t height; /* Height in pixels. Ditto. */ + 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; @@ -139,8 +163,9 @@ struct client bool vertmaxed; /* Vertically maximized? */ bool maxed; /* Totally maximized? */ bool fixed; /* Visible on all workspaces? */ + struct monitor *monitor; /* The physical output this window is on. */ struct item *winitem; /* Pointer to our place in global windows list. */ - struct item *wsitem[WORKSPACE_MAX + 1]; /* Pointer to our place in every + struct item *wsitem[WORKSPACES]; /* Pointer to our place in every * workspace window list. */ }; @@ -151,6 +176,7 @@ 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 @@ -158,13 +184,14 @@ struct client *lastfocuswin; /* Last focused window. NOTE! Only * 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 */ /* * Workspace list: Every workspace has a list of all visible * windows. */ -struct item *wslist[WORKSPACE_MAX + 1] = +struct item *wslist[WORKSPACES] = { NULL, NULL, @@ -210,6 +237,8 @@ struct keys { USERKEY_BOTLEFT, 0 }, { USERKEY_BOTRIGHT, 0 }, { USERKEY_DELETE, 0 }, + { USERKEY_PREVSCREEN, 0 }, + { USERKEY_NEXTSCREEN, 0 }, }; /* All keycodes generating our MODKEY mask. */ @@ -245,51 +274,72 @@ xcb_atom_t wm_protocols; /* WM_PROTOCOLS. */ /* Functions declerations. */ -void finishtabbing(void); -struct modkeycodes getmodkeys(xcb_mod_mask_t modmask); -void cleanup(int code); -void arrangewindows(uint16_t rootwidth, uint16_t rootheight); -void setwmdesktop(xcb_drawable_t win, uint32_t ws); -int32_t getwmdesktop(xcb_drawable_t win); -void addtoworkspace(struct client *client, uint32_t ws); -void delfromworkspace(struct client *client, uint32_t ws); -void changeworkspace(uint32_t ws); -void fixwindow(struct client *client, bool setcolour); -uint32_t getcolor(const char *colstr); -void forgetclient(struct client *client); -void forgetwin(xcb_window_t win); -void newwin(xcb_window_t win); -struct client *setupwin(xcb_window_t win); -xcb_keycode_t keysymtokeycode(xcb_keysym_t keysym, xcb_key_symbols_t *keysyms); -int setupkeys(void); -int setupscreen(void); -void raisewindow(xcb_drawable_t win); -void raiseorlower(struct client *client); -void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y); -struct client *findclient(xcb_drawable_t win); -void focusnext(void); -void setunfocus(xcb_drawable_t win); -void setfocus(struct client *client); -int start_terminal(void); -void resize(xcb_drawable_t win, uint16_t width, uint16_t height); -void resizestep(struct client *client, char direction); -void mousemove(xcb_drawable_t win, int rel_x, int rel_y); -void mouseresize(struct client *client, int rel_x, int rel_y); -void movestep(struct client *client, char direction); -void unmax(struct client *client); -void maximize(struct client *client); -void maxvert(struct client *client); -bool getpointer(xcb_drawable_t win, int16_t *x, int16_t *y); -bool getgeom(xcb_drawable_t win, int16_t *x, int16_t *y, uint16_t *width, - uint16_t *height); -void topleft(void); -void topright(void); -void botleft(void); -void botright(void); -void deletewin(void); -void handle_keypress(xcb_key_press_event_t *ev); -void printhelp(void); -void sigcatch(int sig); +static void finishtabbing(void); +static struct modkeycodes getmodkeys(xcb_mod_mask_t modmask); +static void cleanup(int code); +static void arrangewindows(void); +static void setwmdesktop(xcb_drawable_t win, uint32_t ws); +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); +static void changeworkspace(uint32_t ws); +static void fixwindow(struct client *client, bool setcolour); +static uint32_t getcolor(const char *colstr); +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, 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(void); +static void setunfocus(xcb_drawable_t win); +static void setfocus(struct client *client); +static int start_terminal(void); +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 mousemove(struct client *client, int rel_x, int rel_y); +static void mouseresize(struct client *client, int rel_x, int rel_y); +static void movestep(struct client *client, char direction); +static void setborders(struct client *client, int width); +static void unmax(struct client *client); +static void maximize(struct client *client); +static void maxvert(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 deletewin(void); +static void prevscreen(void); +static void nextscreen(void); +static void handle_keypress(xcb_key_press_event_t *ev); +static void configurerequest(xcb_configure_request_event_t *e); +static void events(void); +static void printhelp(void); +static void sigcatch(int sig); /* Function bodies. */ @@ -428,99 +478,21 @@ void cleanup(int code) } /* - * - * Rearrange windows to fit new screen size rootwidth x rootheight. + * Rearrange windows to fit new screen size. */ -void arrangewindows(uint16_t rootwidth, uint16_t rootheight) +void arrangewindows(void) { - uint32_t mask = 0; - uint32_t values[5]; - bool changed; - int16_t x; - int16_t y; - uint16_t width; - uint16_t height; struct item *item; struct client *client; - - PDEBUG("Rearranging all windows to fit new screen size %d x %d.\n", - rootwidth, rootheight); /* - * Go through all windows we care about and look at their - * coordinates and geometry. If they don't fit on the new screen, + * 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; - - changed = false; - - if (!getgeom(client->id, &x, &y, &width, &height)) - { - return; - } - - PDEBUG("Win %d at %d,%d %d x %d\n", client->id, x, y, width, height); - - if (width > rootwidth) - { - width = rootwidth - BORDERWIDTH * 2; - changed = true; - } - - if (height > rootheight) - { - height = rootheight - BORDERWIDTH * 2; - changed = true; - } - - /* If x or y + geometry is outside of screen, move window. */ - - if (x + width > rootwidth) - { - x = rootwidth - (width + BORDERWIDTH * 2); - changed = true; - } - - if (y + height > rootheight) - { - y = rootheight - (height + BORDERWIDTH * 2);; - changed = true; - } - - /* Reset sense of maximized. */ - client->vertmaxed = false; - - if (client->maxed) - { - client->maxed = false; - - /* Set borders again. */ - values[0] = BORDERWIDTH; - - mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH; - - xcb_configure_window(conn, client->id, mask, &values[0]); - xcb_flush(conn); - } - - if (changed) - { - PDEBUG("--- Win %d going to %d,%d %d x %d\n", client->id, - x, y, width, height); - - mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y - | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - values[0] = x; - values[1] = y; - values[2] = width; - values[3] = height; - - xcb_configure_window(conn, client->id, mask, values); - xcb_flush(conn); - } + fitonscreen(client); } /* for */ } @@ -644,31 +616,14 @@ void changeworkspace(uint32_t ws) } /* Go through list of current ws. Unmap everything that isn't fixed. */ - for (item = wslist[curws]; item != NULL; ) + 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) - { - /* Add the fixed window to the new workspace window list. */ - addtoworkspace(client, ws); - /* - * Remove the fixed window from the current workspace - * list. - * - * NB! Before deleting this item, we need to save the - * address to next item so we can continue through the - * list. - */ - item = item->next; - - delfromworkspace(client, curws); - } - else + if (!client->fixed) { /* * This is an ordinary window. Just unmap it. Note that @@ -676,10 +631,7 @@ void changeworkspace(uint32_t ws) * which we will try to handle later. */ xcb_unmap_window(conn, client->id); - - item = item->next; } - } /* for */ /* Go through list of new ws. Map everything that isn't fixed. */ @@ -709,7 +661,8 @@ void changeworkspace(uint32_t ws) void fixwindow(struct client *client, bool setcolour) { uint32_t values[1]; - + uint32_t ws; + if (NULL == client) { return; @@ -727,7 +680,15 @@ void fixwindow(struct client *client, bool setcolour) xcb_change_window_attributes(conn, client->id, XCB_CW_BORDER_PIXEL, values); } - + + /* Delete from all workspace lists except current. */ + for (ws = 0; ws < WORKSPACES; ws ++) + { + if (ws != curws) + { + delfromworkspace(client, ws); + } + } } else { @@ -741,6 +702,15 @@ void fixwindow(struct client *client, bool setcolour) 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); + } + } + if (setcolour) { /* Set border color to fixed colour. */ @@ -785,18 +755,29 @@ uint32_t getcolor(const char *colstr) /* Forget everything about client client. */ void forgetclient(struct client *client) { + uint32_t ws; + if (NULL == client) { PDEBUG("forgetclient: client was NULL\n"); return; } - - /* Delete window from workspace list. */ - delfromworkspace(client, curws); - free(client->winitem->data); + /* + * 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); + } + } - delitem(&winlist, client->winitem); + /* Remove from global window list. */ + freeitem(&winlist, NULL, client->winitem); } /* Forget everything about a client with client->id win. */ @@ -829,7 +810,7 @@ void forgetwin(xcb_window_t win) * to. Note that it's OK to be on several workspaces at * once. */ - for (ws = 0; ws != WORKSPACE_MAX; ws ++) + for (ws = 0; ws < WORKSPACES; ws ++) { PDEBUG("Looking in ws #%d.\n", ws); if (NULL == client->wsitem[ws]) @@ -853,6 +834,109 @@ void forgetwin(xcb_window_t win) } /* + * Fit client on physical screen, moving and resizing as necessary. + */ +void fitonscreen(struct client *client) +{ + 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, BORDERWIDTH); + } + + 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 outside the physical monitor? */ + 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 + BORDERWIDTH * 2 > mon_width) + { + client->x = mon_x; + client->width = mon_width - BORDERWIDTH * 2;; + willmove = true; + willresize = true; + } + else if (client->x + client->width + BORDERWIDTH * 2 > mon_x + mon_width) + { + client->x = mon_x + mon_width - (client->width + BORDERWIDTH * 2); + willmove = true; + } + + if (client->height + BORDERWIDTH * 2 > mon_height) + { + client->y = mon_y; + client->height = mon_height - BORDERWIDTH * 2; + willmove = true; + willresize = true; + } + else if (client->y + client->height + BORDERWIDTH * 2 > mon_y + mon_height) + { + client->y = mon_y + mon_height - (client->height + 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); + } +} + +/* * Set position, geometry and attributes of a new window and show it * on the screen. */ @@ -860,10 +944,6 @@ void newwin(xcb_window_t win) { int16_t pointx; int16_t pointy; - int16_t x; - int16_t y; - uint16_t width; - uint16_t height; struct client *client; if (NULL != findclient(win)) @@ -876,11 +956,11 @@ void newwin(xcb_window_t win) */ return; } - + /* 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; } @@ -889,7 +969,6 @@ void newwin(xcb_window_t win) * Set up stuff, like borders, add the window to the client list, * et cetera. */ - client = setupwin(win); if (NULL == client) { @@ -900,63 +979,43 @@ void newwin(xcb_window_t win) /* Add this window to the current workspace. */ addtoworkspace(client, curws); - if (!getgeom(win, &x, &y, &width, &height)) - { - PDEBUG("Couldn't get geometry\n"); - return; - } - - /* - * If the client says the user specified the coordinates, we - * override the pointer position and place the window where the - * client specifies instead. - */ - if (client->usercoord) - { - pointx = x; - pointy = y; - } - /* - * If the window is larger than our screen, just place it in the - * corner and resize. + * If the client doesn't say the user specified the coordinates + * for the window we store it where our pointer is instead. */ - if (width > screen->width_in_pixels) + if (!client->usercoord) { - pointx = 0; - width = screen->width_in_pixels - BORDERWIDTH * 2;; - resize(win, width, height); - } - else if (pointx + width + BORDERWIDTH * 2 > screen->width_in_pixels) - { - pointx = screen->width_in_pixels - (width + BORDERWIDTH * 2); - } + PDEBUG("Coordinates not set by user. Using pointer: %d,%d.\n", + pointx, pointy); + client->x = pointx; + client->y = pointy; - if (height > screen->height_in_pixels) + movewindow(client->id, client->x, client->y); + } + else { - pointy = 0; - height = screen->height_in_pixels - BORDERWIDTH * 2; - resize(win, width, height); + PDEBUG("User set coordinates.\n"); } - else if (pointy + height + BORDERWIDTH * 2 > screen->height_in_pixels) + + /* Find the physical output this window will be on if RANDR is active. */ + if (-1 != randrbase) { - pointy = screen->height_in_pixels - (height + BORDERWIDTH * 2); + client->monitor = findmonbycoord(pointx, pointy); } - /* Move the window to cursor position. */ - movewindow(win, pointx, pointy); + fitonscreen(client); /* Show window on screen. */ - xcb_map_window(conn, win); + xcb_map_window(conn, client->id); /* * 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, - width / 2, height / 2); - - xcb_flush(conn); + client->width / 2, client->height / 2); + + xcb_flush(conn); } /* Set border colour, width and event mask for window. */ @@ -1005,7 +1064,7 @@ struct client *setupwin(xcb_window_t win) } item->data = client; - + /* Initialize client. */ client->id = win; client->usercoord = false; @@ -1024,15 +1083,24 @@ struct client *setupwin(xcb_window_t win) client->vertmaxed = false; client->maxed = false; client->fixed = false; + client->monitor = NULL; + client->winitem = item; - for (ws = 0; ws != WORKSPACE_MAX; ws ++) + 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. */ @@ -1040,7 +1108,7 @@ struct client *setupwin(xcb_window_t win) conn, xcb_get_wm_normal_hints_unchecked( conn, win), &hints, NULL)) { - PDEBUG("Couldn't get size hints."); + PDEBUG("Couldn't get size hints.\n"); } /* @@ -1242,6 +1310,31 @@ int setupscreen(void) if (NULL != client) { /* + * Find 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. * @@ -1250,10 +1343,12 @@ int setupscreen(void) if (ws == NET_WM_FIXED) { + /* Add to current workspace. */ + addtoworkspace(client, curws); + /* Add to all other workspaces. */ fixwindow(client, false); - addtoworkspace(client, curws); } - else if (MCWM_NOWS != ws && ws < WORKSPACE_MAX) + else if (MCWM_NOWS != ws && ws < WORKSPACES) { addtoworkspace(client, ws); /* If it's not our current workspace, hide it. */ @@ -1302,6 +1397,407 @@ int setupscreen(void) return 0; } +/* + * Set up RANDR extension. Get the extension base and subscribe to + * events. + */ +int setuprandr(void) +{ + const xcb_query_extension_reply_t *extension; + int base; + + extension = xcb_get_extension_data(conn, &xcb_randr_id); + if (!extension->present) + { + printf("No RANDR.\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; +} + +/* + * Get RANDR resources and figure out how many outputs there are. + */ +void getrandr(void) +{ + 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); +} + +/* + * Walk through all the RANDR outputs (number of outputs == len) there + * was at time timestamp. + */ +void getoutputs(xcb_randr_output_t *outputs, int len, xcb_timestamp_t 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)); + +#if 0 + + output: + + uint8_t response_type; /**< */ + uint8_t status; /**< */ + uint16_t sequence; /**< */ + uint32_t length; /**< */ + xcb_timestamp_t timestamp; /**< */ + xcb_randr_crtc_t crtc; /**< */ + uint32_t mm_width; /**< */ + uint32_t mm_height; /**< */ + uint8_t connection; /**< */ + uint8_t subpixel_order; /**< */ + uint16_t num_crtcs; /**< */ + uint16_t num_modes; /**< */ + uint16_t num_preferred; /**< */ + uint16_t num_clones; /**< */ + uint16_t name_len; /**< */ +#endif + + 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; + } + +#if 0 +typedef struct xcb_randr_get_crtc_info_reply_t { + uint8_t response_type; /**< */ + uint8_t status; /**< */ + uint16_t sequence; /**< */ + uint32_t length; /**< */ + xcb_timestamp_t timestamp; /**< */ + int16_t x; /**< */ + int16_t y; /**< */ + uint16_t width; /**< */ + uint16_t height; /**< */ + xcb_randr_mode_t mode; /**< */ + uint16_t rotation; /**< */ + uint16_t rotations; /**< */ + uint16_t num_outputs; /**< */ + uint16_t num_possible_outputs; /**< */ +} xcb_randr_get_crtc_info_reply_t; + +#endif + + 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->data) + { + 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; +} + /* Raise window win to top of stack. */ void raisewindow(xcb_drawable_t win) { @@ -1340,6 +1836,51 @@ void raiseorlower(struct client *client) xcb_flush(conn); } +void movelim(struct client *client) +{ + 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 outside the physical monitor? */ + if (client->x < mon_x) + { + client->x = mon_x; + } + if (client->y < mon_y) + { + client->y = mon_y; + } + + if (client->x + client->width > mon_x + mon_width - BORDERWIDTH * 2) + { + client->x = (mon_x + mon_width - BORDERWIDTH * 2) - client->width; + } + + if (client->y + client->height > mon_y + mon_height - BORDERWIDTH * 2) + { + client->y = (mon_y + mon_height - BORDERWIDTH * 2) - client->height; + } + + movewindow(client->id, client->x, client->y); +} + /* Move window win to root coordinates x,y. */ void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y) { @@ -1625,6 +2166,53 @@ int start_terminal(void) return 0; } +/* Resize with limit. */ +void resizelim(struct client *client) +{ + 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 + BORDERWIDTH * 2 > mon_x + mon_width) + { + client->width = mon_width - ((client->x - mon_x) + BORDERWIDTH * 2); + } + + if (client->y + client->height + BORDERWIDTH * 2 > mon_y + mon_height) + { + client->height = mon_height - ((client->y - mon_y) + BORDERWIDTH * 2); + } + + resize(client->id, client->width, client->height); +} + /* Resize window win to width,height. */ void resize(xcb_drawable_t win, uint16_t width, uint16_t height) { @@ -1635,6 +2223,8 @@ void resize(xcb_drawable_t win, uint16_t width, uint16_t height) /* Can't resize root. */ return; } + + PDEBUG("Resizing to %d x %d.\n", width, height); values[0] = width; values[1] = height; @@ -1658,18 +2248,8 @@ void resize(xcb_drawable_t win, uint16_t width, uint16_t height) */ void resizestep(struct client *client, char direction) { - int16_t start_x; - int16_t start_y; - int16_t x; - int16_t y; - uint16_t width; - uint16_t height; - uint16_t origwidth; - uint16_t origheight; int step_x = MOVE_STEP; int step_y = MOVE_STEP; - xcb_drawable_t win; - bool warp = false; if (NULL == client) { @@ -1682,25 +2262,8 @@ void resizestep(struct client *client, char direction) return; } - win = client->id; - - /* Save pointer position so we can warp back later, if necessary. */ - if (!getpointer(win, &start_x, &start_y)) - { - return; - } + raisewindow(client->id); - raisewindow(win); - - /* Get window geometry. */ - if (!getgeom(client->id, &x, &y, &width, &height)) - { - return; - } - - origwidth = width; - origheight = height; - if (client->width_inc > 1) { step_x = client->width_inc; @@ -1718,44 +2281,23 @@ void resizestep(struct client *client, char direction) { step_y = MOVE_STEP; } - + switch (direction) { case 'h': - if (step_x >= width) - { - return; - } - - width = width - step_x; - height = height; - + client->width = client->width - step_x; break; case 'j': - width = width; - height = height + step_y; - if (height + y > screen->height_in_pixels) - { - return; - } + client->height = client->height + step_y; break; case 'k': - if (step_y >= height) - { - return; - } - height = height - step_y; + client->height = client->height - step_y; break; case 'l': - width = width + step_x; - height = height; - if (width + x > screen->width_in_pixels) - { - return; - } + client->width = client->width + step_x; break; default: @@ -1763,174 +2305,46 @@ void resizestep(struct client *client, char direction) break; } /* switch direction */ - /* Is it smaller than it wants to be? */ - if (0 != client->min_height && height < client->min_height) - { - height = client->min_height; - } - - if (0 != client->min_width && width < client->min_width) - { - width = client->min_width; - } + resizelim(client); - PDEBUG("Resizing to %dx%d\n", width, height); - resize(win, width, height); - /* If this window was vertically maximized, remember that it isn't now. */ if (client->vertmaxed) { client->vertmaxed = false; } - - /* - * We might need to warp the pointer to keep the focus. - * - * Don't do anything if the pointer was outside the window when we - * began resizing. - * - * If the pointer was inside the window when we began and it still - * is, don't do anything. However, if we're about to lose the - * pointer, move in. - */ - if (start_x > 0 - BORDERWIDTH && start_x < origwidth + BORDERWIDTH - && start_y > 0 - BORDERWIDTH && start_y < origheight + BORDERWIDTH ) - { - x = start_x; - y = start_y; - - if (start_x > width - step_x) - { - x = width / 2; - if (0 == x) - { - x = 1; - } - warp = true; - } - - if (start_y > height - step_y) - { - y = height / 2; - if (0 == y) - { - y = 1; - } - warp = true; - } - if (warp) - { - xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0, - x, y); - xcb_flush(conn); - } - } + xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, + client->width / 2, client->height / 2); + xcb_flush(conn); } /* * Move window win as a result of pointer motion to coordinates * rel_x,rel_y. */ -void mousemove(xcb_drawable_t win, int rel_x, int rel_y) +void mousemove(struct client *client, int rel_x, int rel_y) { - xcb_get_geometry_reply_t *geom; - int x; - int y; - - /* Get window geometry. */ - - geom = xcb_get_geometry_reply(conn, - xcb_get_geometry(conn, win), - NULL); - if (NULL == geom) - { - return; - } - - x = rel_x; - y = rel_y; - - if (x < 0) - { - x = 0; - } - if (y < 0) - { - y = 0; - } - if (y + geom->height + BORDERWIDTH * 2 > screen->height_in_pixels) - { - y = screen->height_in_pixels - (geom->height + BORDERWIDTH * 2); - } - if (x + geom->width + BORDERWIDTH * 2 > screen->width_in_pixels) - { - x = screen->width_in_pixels - (geom->width + BORDERWIDTH * 2); - } + client->x = rel_x; + client->y = rel_y; - movewindow(win, x, y); - - free(geom); + movelim(client); } void mouseresize(struct client *client, int rel_x, int rel_y) { - uint16_t width; - uint16_t height; - int16_t x; - int16_t y; - - /* Get window geometry. We throw away width and height values. */ - if (!getgeom(client->id, &x, &y, &width, &height)) - { - return; - } + client->width = abs(rel_x - client->x); + client->height = abs(rel_y - client->y); - /* - * Calculate new width and height. If we have WM hints, we use - * them. Otherwise these are set to 1 pixel when initializing - * client. - * - * Note that we need to take the absolute of the difference since - * we're dealing with unsigned integers. This has the interesting - * side effect that we resize the window even if the mouse pointer - * is at the other side of the window. - */ - - width = abs(rel_x - x); - height = abs(rel_y - y); - - width -= (width - client->base_width) % client->width_inc; - height -= (height - client->base_height) % client->height_inc; + client->width -= (client->width - client->base_width) % client->width_inc; + client->height -= (client->height - client->base_height) + % client->height_inc; - /* Is it smaller than it wants to be? */ - if (0 != client->min_height && height < client->min_height) - { - height = client->min_height; - } + 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); - if (0 != client->min_width && width < client->min_width) - { - width = client->min_width; - } - - /* Check if the window fits on screen. */ - if (x + width > screen->width_in_pixels - BORDERWIDTH * 2) - { - width = screen->width_in_pixels - (x + BORDERWIDTH * 2); - } - - if (y + height > screen->height_in_pixels - BORDERWIDTH * 2) - { - height = screen->height_in_pixels - (y + BORDERWIDTH * 2); - } + resizelim(client); - PDEBUG("Resizing to %dx%d (%dx%d)\n", width, height, - (width - client->base_width) / client->width_inc, - (height - client->base_height) / client->height_inc); - - resize(client->id, width, height); - /* If this window was vertically maximized, remember that it isn't now. */ if (client->vertmaxed) { @@ -1942,11 +2356,6 @@ void movestep(struct client *client, char direction) { int16_t start_x; int16_t start_y; - int16_t x; - int16_t y; - uint16_t width; - uint16_t height; - xcb_drawable_t win; if (NULL == client) { @@ -1959,63 +2368,29 @@ void movestep(struct client *client, char direction) return; } - win = client->id; - /* Save pointer position so we can warp pointer here later. */ - if (!getpointer(win, &start_x, &start_y)) - { - return; - } - - if (!getgeom(win, &x, &y, &width, &height)) + if (!getpointer(client->id, &start_x, &start_y)) { return; } - width = width + BORDERWIDTH * 2; - height = height + BORDERWIDTH * 2; - - raisewindow(win); - + raisewindow(client->id); switch (direction) { case 'h': - x = x - MOVE_STEP; - if (x < 0) - { - x = 0; - } - - movewindow(win, x, y); + client->x = client->x - MOVE_STEP; break; case 'j': - y = y + MOVE_STEP; - if (y + height > screen->height_in_pixels) - { - y = screen->height_in_pixels - height; - } - movewindow(win, x, y); + client->y = client->y + MOVE_STEP; break; case 'k': - y = y - MOVE_STEP; - if (y < 0) - { - y = 0; - } - - movewindow(win, x, y); + client->y = client->y - MOVE_STEP; break; case 'l': - x = x + MOVE_STEP; - if (x + width > screen->width_in_pixels) - { - x = screen->width_in_pixels - width; - } - - movewindow(win, x, y); + client->x = client->x + MOVE_STEP; break; default: @@ -2023,19 +2398,33 @@ void movestep(struct client *client, char direction) 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 - BORDERWIDTH && start_x < width + BORDERWIDTH - && start_y > 0 - BORDERWIDTH && start_y < height + BORDERWIDTH ) + if (start_x > 0 - BORDERWIDTH && start_x < client->width + BORDERWIDTH + && start_y > 0 - BORDERWIDTH && start_y < client->height + BORDERWIDTH ) { - xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0, + xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, start_x, start_y); xcb_flush(conn); } } +void setborders(struct client *client, int width) +{ + uint32_t values[1]; + uint32_t mask = 0; + + values[0] = width; + + mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH; + xcb_configure_window(conn, client->id, mask, &values[0]); + xcb_flush(conn); +} + void unmax(struct client *client) { uint32_t values[5]; @@ -2046,13 +2435,17 @@ void unmax(struct client *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) { - values[0] = client->x; - values[1] = client->y; + values[1] = client->y; values[2] = client->width; values[3] = client->height; @@ -2081,16 +2474,19 @@ void unmax(struct client *client) /* Warp pointer to window or we might lose it. */ xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0, - 1, 1); + client->width / 2, client->height / 2); xcb_flush(conn); } void maximize(struct client *client) { - xcb_get_geometry_reply_t *geom; uint32_t values[4]; - uint32_t mask = 0; + uint32_t mask = 0; + int16_t mon_x; + int16_t mon_y; + uint16_t mon_width; + uint16_t mon_height; if (NULL == client) { @@ -2098,6 +2494,21 @@ void maximize(struct client *client) 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. @@ -2108,35 +2519,31 @@ void maximize(struct client *client) client->maxed = false; return; } - - /* Get window geometry. */ - geom = xcb_get_geometry_reply(conn, - xcb_get_geometry(conn, client->id), - NULL); - if (NULL == geom) - { - return; - } /* Raise first. Pretty silly to maximize below something else. */ raisewindow(client->id); /* FIXME: Store original geom in property as well? */ - client->x = geom->x; - client->y = geom->y; - client->width = geom->width; - client->height = geom->height; - + 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. */ - values[0] = 0; - values[1] = 0; - values[2] = screen->width_in_pixels; - values[3] = screen->height_in_pixels; + client->x = mon_x; + client->y = mon_y; + client->width = mon_width; + client->height = mon_height; + + 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 @@ -2145,17 +2552,15 @@ void maximize(struct client *client) xcb_flush(conn); client->maxed = true; - - free(geom); } void maxvert(struct client *client) { uint32_t values[2]; - uint16_t width; - uint16_t height; - int16_t x; - int16_t y; + int16_t mon_x; + int16_t mon_y; + uint16_t mon_width; + uint16_t mon_height; if (NULL == client) { @@ -2163,6 +2568,21 @@ void maxvert(struct client *client) 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. */ @@ -2175,29 +2595,25 @@ void maxvert(struct client *client) /* Raise first. Pretty silly to maximize below something else. */ raisewindow(client->id); - - /* Get window geometry. */ - if (!getgeom(client->id, &x, &y, &width, &height)) - { - return; - } /* * Store original coordinates and geometry. * FIXME: Store in property as well? */ - client->x = x; - client->y = y; - client->width = width; - client->height = height; + client->origsize.x = client->x; + client->origsize.y = client->y; + client->origsize.width = client->width; + client->origsize.height = client->height; + client->y = mon_y; /* Compute new height considering height increments and screen height. */ - height = screen->height_in_pixels - BORDERWIDTH * 2; - height -= (height - client->base_height) % client->height_inc; + client->height = mon_height - BORDERWIDTH * 2; + client->height -= (client->height - client->base_height) + % client->height_inc; /* Move to top of screen and resize. */ - values[0] = 0; - values[1] = height; + values[0] = client->y; + values[1] = client->height; xcb_configure_window(conn, client->id, XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_HEIGHT, values); @@ -2254,20 +2670,35 @@ void topleft(void) { int16_t pointx; int16_t pointy; - + int16_t mon_x; + int16_t mon_y; + if (NULL == focuswin) { 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; } - - movewindow(focuswin->id, 0, 0); + + 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); @@ -2275,32 +2706,38 @@ void topleft(void) void topright(void) { - int16_t x; - int16_t y; - uint16_t width; - uint16_t height; int16_t pointx; int16_t pointy; - + uint16_t mon_y; + uint16_t mon_width; + if (NULL == focuswin) { return; } - raisewindow(focuswin->id); - - if (!getpointer(focuswin->id, &pointx, &pointy)) + if (NULL == focuswin->monitor) { - return; + mon_width = screen->width_in_pixels; + mon_y = 0; + } + else + { + mon_width = focuswin->monitor->width; + mon_y = focuswin->monitor->y; } + + raisewindow(focuswin->id); - if (!getgeom(focuswin->id, &x, &y, &width, &height)) + if (!getpointer(focuswin->id, &pointx, &pointy)) { return; } - movewindow(focuswin->id, screen->width_in_pixels - - (width + BORDERWIDTH * 2), 0); + focuswin->x = mon_width - (focuswin->width + 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); @@ -2310,32 +2747,41 @@ void topright(void) void botleft(void) { - int16_t x; - int16_t y; - uint16_t width; - uint16_t height; int16_t pointx; int16_t pointy; - + int16_t mon_x; + int16_t mon_y; + uint16_t mon_height; + if (NULL == focuswin) { return; } - raisewindow(focuswin->id); - - if (!getpointer(focuswin->id, &pointx, &pointy)) + if (NULL == focuswin->monitor) { - return; + mon_x = 0; + mon_y = 0; + mon_height = screen->height_in_pixels; + } + else + { + mon_x = focuswin->monitor->x; + mon_y = focuswin->monitor->y; + mon_height = focuswin->monitor->height; } - if (!getgeom(focuswin->id, &x, &y, &width, &height)) + raisewindow(focuswin->id); + + if (!getpointer(focuswin->id, &pointx, &pointy)) { return; } - - movewindow(focuswin->id, 0, screen->height_in_pixels - - (height + BORDERWIDTH * 2)); + + focuswin->x = mon_x; + focuswin->y = mon_y + mon_height - (focuswin->height + BORDERWIDTH * 2); + + movewindow(focuswin->id, focuswin->x, focuswin->y); xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, pointx, pointy); @@ -2344,35 +2790,45 @@ void botleft(void) void botright(void) { - int16_t x; - int16_t y; - uint16_t width; - uint16_t height; 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) { return; } - raisewindow(focuswin->id); - - if (!getpointer(focuswin->id, &pointx, &pointy)) + if (NULL == focuswin->monitor) { - return; + mon_x = 0; + mon_y = 0; + mon_width = screen->width_in_pixels;; + mon_height = screen->height_in_pixels; } + else + { + mon_x = focuswin->monitor->x; + mon_y = focuswin->monitor->y; + mon_width = focuswin->monitor->width; + mon_height = focuswin->monitor->height; + } + + raisewindow(focuswin->id); - if (!getgeom(focuswin->id, &x, &y, &width, &height)) + if (!getpointer(focuswin->id, &pointx, &pointy)) { return; } + + focuswin->x = mon_x + mon_width - (focuswin->width + BORDERWIDTH * 2); + + focuswin->y = mon_y + mon_height - (focuswin->height + BORDERWIDTH * 2); - movewindow(focuswin->id, - screen->width_in_pixels - - (width + BORDERWIDTH * 2), - screen->height_in_pixels - - (height + BORDERWIDTH * 2)); + movewindow(focuswin->id, focuswin->x, focuswin->y); xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, pointx, pointy); @@ -2423,6 +2879,60 @@ void deletewin(void) 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; + } + + focuswin->monitor = item->data; + + raisewindow(focuswin->id); + 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; + } + + focuswin->monitor = item->data; + + raisewindow(focuswin->id); + fitonscreen(focuswin); + movelim(focuswin); + + xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0, + 0, 0); + xcb_flush(conn); +} + void handle_keypress(xcb_key_press_event_t *ev) { int i; @@ -2584,7 +3094,15 @@ void handle_keypress(xcb_key_press_event_t *ev) case KEY_END: deletewin(); break; - + + case KEY_PREVSCR: + prevscreen(); + break; + + case KEY_NEXTSCR: + nextscreen(); + break; + default: /* Ignore other keys. */ break; @@ -2592,6 +3110,165 @@ void handle_keypress(xcb_key_press_event_t *ev) } } /* handle_keypress() */ +void configurerequest(xcb_configure_request_event_t *e) +{ + uint32_t mask = 0; + uint32_t values[7]; + int i = -1; + struct client *client; + 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. */ + client = findclient(e->window); + if (NULL == client) + { + PDEBUG("We don't know about this window yet.\n"); + } + + 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; + } + + /* + * We ignore border width configurations, but handle all + * others. + */ + + if (e->value_mask & XCB_CONFIG_WINDOW_X) + { + PDEBUG("Changing X coordinate to %d\n", e->x); + mask |= XCB_CONFIG_WINDOW_X; + i ++; + + if (client) + { + client->x = e->x; + if (client->x < mon_x) + { + client->x = mon_x; + } + else if (client->x + client->width > mon_x + mon_width) + { + client->x = (mon_x + mon_width) - client->width; + } + + values[i] = client->x; + } + else + { + values[i] = e->x; + } + } + + if (e->value_mask & XCB_CONFIG_WINDOW_Y) + { + PDEBUG("Changing Y coordinate to %d.\n", e->y); + mask |= XCB_CONFIG_WINDOW_Y; + i ++; + + if (client) + { + client->y = e->y; + if (client->y < mon_y) + { + client->y = mon_y; + } + else if (client->y + client->height > mon_y + mon_height) + { + client->y = (mon_y + mon_height) - client->height; + } + + values[i] = client->y; + } + else + { + values[i] = e->y; + } + } + + if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) + { + PDEBUG("Changing width to %d.\n", e->width); + mask |= XCB_CONFIG_WINDOW_WIDTH; + i ++; + + if (client) + { + client->width = e->width; + + if (client->width + BORDERWIDTH * 2 > mon_width) + { + client->width = mon_width - BORDERWIDTH * 2; + } + + values[i] = client->width; + } + else + { + values[i] = e->width; + } + } + + if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) + { + PDEBUG("Changing height to %d.\n", e->height); + mask |= XCB_CONFIG_WINDOW_HEIGHT; + i ++; + + if (client) + { + client->height = e->height; + if (client->height + BORDERWIDTH * 2 > mon_height) + { + client->height = mon_height - BORDERWIDTH * 2; + } + + values[i] = client->height; + } + else + { + values[i] = e->height; + } + } + + if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) + { + mask |= XCB_CONFIG_WINDOW_SIBLING; + i ++; + values[i] = e->sibling; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) + { + PDEBUG("Changing stack order.\n"); + mask |= XCB_CONFIG_WINDOW_STACK_MODE; + i ++; + values[i] = e->stack_mode; + } + + if (-1 != i) + { + xcb_configure_window(conn, e->window, mask, values); + xcb_flush(conn); + } +} + void events(void) { xcb_generic_event_t *ev; @@ -2657,6 +3334,16 @@ void events(void) } #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_MAP_REQUEST: @@ -2708,10 +3395,6 @@ void events(void) case XCB_BUTTON_PRESS: { xcb_button_press_event_t *e; - int16_t x; - int16_t y; - uint16_t width; - uint16_t height; e = (xcb_button_press_event_t *) ev; PDEBUG("Button %d pressed in window %ld, subwindow %d " @@ -2765,13 +3448,7 @@ void events(void) /* Raise window. */ raisewindow(focuswin->id); - - /* Get window geometry. */ - if (!getgeom(focuswin->id, &x, &y, &width, &height)) - { - break; - } - + /* Mouse button 1 was pressed. */ if (1 == e->detail) { @@ -2792,7 +3469,7 @@ void events(void) /* Warp pointer to lower right. */ xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, - 0, width, height); + 0, focuswin->width, focuswin->height); } /* @@ -2808,7 +3485,8 @@ void events(void) */ xcb_grab_pointer(conn, 0, screen->root, XCB_EVENT_MASK_BUTTON_RELEASE - | XCB_EVENT_MASK_BUTTON_MOTION, + | XCB_EVENT_MASK_BUTTON_MOTION + | XCB_EVENT_MASK_POINTER_MOTION_HINT, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root, @@ -2824,7 +3502,7 @@ void events(void) case XCB_MOTION_NOTIFY: { - xcb_motion_notify_event_t *e; + xcb_query_pointer_reply_t *pointer; /* * We can't do anything if we don't have a focused window @@ -2834,26 +3512,41 @@ void events(void) { break; } - - e = (xcb_motion_notify_event_t *) ev; /* + * 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->id, e->root_x, e->root_y); + mousemove(focuswin, pointer->root_x, pointer->root_y); } else if (mode == MCWM_RESIZE) { - mouseresize(focuswin, e->root_x, e->root_y); + mouseresize(focuswin, pointer->root_x, pointer->root_y); } else { PDEBUG("Motion event when we're not moving our resizing!\n"); } + + free(pointer); } + break; case XCB_BUTTON_RELEASE: @@ -2871,8 +3564,6 @@ void events(void) { int16_t x; int16_t y; - uint16_t width; - uint16_t height; /* We're finished moving or resizing. */ @@ -2897,20 +3588,14 @@ void events(void) * 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. - */ - if (!getgeom(focuswin->id, &x, &y, &width, &height)) - { - break; - } - - /* + * * Move to saved position within window or if that * position is now outside current window, move inside * window. */ - if (mode_x > width) + if (mode_x > focuswin->width) { - x = width / 2; + x = focuswin->width / 2; if (0 == x) { x = 1; @@ -2922,9 +3607,9 @@ void events(void) x = mode_x; } - if (mode_y > height) + if (mode_y > focuswin->height) { - y = height / 2; + y = focuswin->height / 2; if (0 == y) { y = 1; @@ -2937,7 +3622,6 @@ void events(void) 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! */ @@ -3064,15 +3748,14 @@ void events(void) if (e->window == screen->root) { /* - * When using RANDR, 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. + * 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, do nothing. - * + * unchanged we do nothing. */ PDEBUG("Notify event for root!\n"); PDEBUG("Possibly a new root geometry: %dx%d\n", @@ -3086,82 +3769,28 @@ void events(void) } else { - arrangewindows(e->width, e->height); 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: - { - xcb_configure_request_event_t *e - = (xcb_configure_request_event_t *)ev; - uint32_t mask = 0; - uint32_t values[7]; - int i = -1; - - PDEBUG("event: Configure request. mask = %d\n", e->value_mask); - - /* - * We ignore border width configurations, but handle all - * others. - */ - - if (e->value_mask & XCB_CONFIG_WINDOW_X) - { - PDEBUG("Changing X coordinate to %d\n", e->x); - mask |= XCB_CONFIG_WINDOW_X; - i ++; - values[i] = e->x; - } - - if (e->value_mask & XCB_CONFIG_WINDOW_Y) - { - PDEBUG("Changing Y coordinate to %d.\n", e->y); - mask |= XCB_CONFIG_WINDOW_Y; - i ++; - values[i] = e->y; - } - - if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) - { - PDEBUG("Changing width to %d.\n", e->width); - mask |= XCB_CONFIG_WINDOW_WIDTH; - i ++; - values[i] = e->width; - } - - if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) - { - PDEBUG("Changing height to %d.\n", e->height); - mask |= XCB_CONFIG_WINDOW_HEIGHT; - i ++; - values[i] = e->height; - } - - if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) - { - mask |= XCB_CONFIG_WINDOW_SIBLING; - i ++; - values[i] = e->sibling; - } - - if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) - { - PDEBUG("Changing stack order.\n"); - mask |= XCB_CONFIG_WINDOW_STACK_MODE; - i ++; - values[i] = e->stack_mode; - } - - if (-1 != i) - { - xcb_configure_window(conn, e->window, mask, values); - xcb_flush(conn); - } - } + configurerequest((xcb_configure_request_event_t *) ev); break; case XCB_CIRCULATE_REQUEST: @@ -3240,8 +3869,14 @@ void events(void) if (client->id == e->window) { PDEBUG("Forgetting about %d\n", e->window); + if (focuswin == client) + { + focuswin = NULL; + } + forgetclient(client); - break; + /* We're finished. Break out of for loop. */ + break; } } /* for */ } @@ -3373,7 +4008,10 @@ int main(int argc, char **argv) atom_desktop = xcb_atom_get(conn, "_NET_WM_DESKTOP"); wm_delete_window = xcb_atom_get(conn, "WM_DELETE_WINDOW"); wm_protocols = xcb_atom_get(conn, "WM_PROTOCOLS"); - + + /* Check for RANDR extension and configure. */ + randrbase = setuprandr(); + /* Loop over all clients and set up stuff. */ if (0 != setupscreen()) { |