diff options
Diffstat (limited to 'src/clickety.c')
-rw-r--r-- | src/clickety.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/clickety.c b/src/clickety.c new file mode 100644 index 0000000..a64e6d7 --- /dev/null +++ b/src/clickety.c @@ -0,0 +1,244 @@ +/* + * \/\/\ + * + * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + /* input event handling stuff */ + +#include <X11/Xlib.h> + +#include "clickety.h" +#include "desktop.h" +#include "vwm.h" +#include "window.h" +#include "xwindow.h" + +/* simple little state machine for managing mouse click/drag/release sequences so we don't need another event loop */ +typedef enum _vwm_adjust_mode_t { + VWM_ADJUST_RESIZE, + VWM_ADJUST_MOVE +} vwm_adjust_mode_t; + +typedef struct _vwm_clickety_t { + vwm_window_t *vwin; + vwm_adjust_mode_t mode; + XWindowAttributes orig, lastrect; + int impetus_x, impetus_y, impetus_x_root, impetus_y_root; +} vwm_clickety_t; + +static vwm_clickety_t clickety; + +/* helper function for resizing the window, how the motion is applied depends on where in the window the impetus event occurred */ +static void compute_resize(XEvent *terminus, XWindowAttributes *new) +{ + vwm_window_t *vwin; + int dw = (clickety.orig.width / 2); + int dh = (clickety.orig.height / 2); + int xdelta = (terminus->xbutton.x_root - clickety.impetus_x_root); + int ydelta = (terminus->xbutton.y_root - clickety.impetus_y_root); + int min_width = 0, min_height = 0; + int width_inc = 1, height_inc = 1; + + /* TODO: there's a problem here WRT border width, I should be considering it, I just haven't bothered to fix it since it doesn't seem to matter */ + if ((vwin = clickety.vwin) && vwin->hints) { + if ((vwin->hints_supplied & PMinSize)) { + min_width = vwin->hints->min_width; + min_height = vwin->hints->min_height; + VWM_TRACE("window size hints exist and minimum sizes are w=%i h=%i", min_width, min_height); + } + + if ((vwin->hints_supplied & PResizeInc)) { + width_inc = vwin->hints->width_inc; + height_inc = vwin->hints->height_inc; + VWM_TRACE("window size hints exist and resize increments are w=%i h=%i", width_inc, height_inc); + if (!width_inc) width_inc = 1; + if (!height_inc) height_inc = 1; + } + } + + xdelta = xdelta / width_inc * width_inc; + ydelta = ydelta / height_inc * height_inc; + + if (clickety.impetus_x < dw && clickety.impetus_y < dh) { + /* grabbed top left */ + new->x = clickety.orig.x + xdelta; + new->y = clickety.orig.y + ydelta; + new->width = clickety.orig.width - xdelta; + new->height = clickety.orig.height - ydelta; + } else if (clickety.impetus_x > dw && clickety.impetus_y < dh) { + /* grabbed top right */ + new->x = clickety.orig.x; + new->y = clickety.orig.y + ydelta; + new->width = clickety.orig.width + xdelta; + new->height = clickety.orig.height - ydelta; + } else if (clickety.impetus_x < dw && clickety.impetus_y > dh) { + /* grabbed bottom left */ + new->x = clickety.orig.x + xdelta; + new->y = clickety.orig.y; + new->width = clickety.orig.width - xdelta; + new->height = clickety.orig.height + ydelta; + } else if (clickety.impetus_x > dw && clickety.impetus_y > dh) { + /* grabbed bottom right */ + new->x = clickety.orig.x; + new->y = clickety.orig.y; + new->width = clickety.orig.width + xdelta; + new->height = clickety.orig.height + ydelta; + } + + /* constrain the width and height of the window according to the minimums */ + if (new->width < min_width) { + if (clickety.orig.x != new->x) new->x -= (min_width - new->width); + new->width = min_width; + } + + if (new->height < min_height) { + if (clickety.orig.y != new->y) new->y -= (min_height - new->height); + new->height = min_height; + } +} + + +void vwm_clickety_motion(vwm_t *vwm, Window win, XMotionEvent *motion) +{ + XWindowChanges changes = { .border_width = WINDOW_BORDER_WIDTH }; + + if (!clickety.vwin) return; + + /* TODO: verify win matches clickety.vwin? */ + switch (clickety.mode) { + case VWM_ADJUST_MOVE: + changes.x = clickety.orig.x + (motion->x_root - clickety.impetus_x_root); + changes.y = clickety.orig.y + (motion->y_root - clickety.impetus_y_root); + XConfigureWindow(vwm->display, win, CWX | CWY | CWBorderWidth, &changes); + break; + + case VWM_ADJUST_RESIZE: { + XWindowAttributes resized; + + /* XXX: it just so happens the XMotionEvent structure is identical to XButtonEvent in the fields + * needed by compute_resize... */ + compute_resize((XEvent *)motion, &resized); + /* TODO: this is probably broken with compositing active, but it doesn't seem to be too messed up in practice */ + /* erase the last rectangle */ + XDrawRectangle(vwm->display, VWM_XROOT(vwm), vwm->gc, clickety.lastrect.x, clickety.lastrect.y, clickety.lastrect.width, clickety.lastrect.height); + /* draw a frame @ resized coordinates */ + XDrawRectangle(vwm->display, VWM_XROOT(vwm), vwm->gc, resized.x, resized.y, resized.width, resized.height); + /* remember the last rectangle */ + clickety.lastrect = resized; + break; + } + + default: + break; + } +} + + +void vwm_clickety_released(vwm_t *vwm, Window win, XButtonPressedEvent *terminus) +{ + XWindowChanges changes = { .border_width = WINDOW_BORDER_WIDTH }; + + if (!clickety.vwin) return; + + switch (clickety.mode) { + case VWM_ADJUST_MOVE: + changes.x = clickety.orig.x + (terminus->x_root - clickety.impetus_x_root); + changes.y = clickety.orig.y + (terminus->y_root - clickety.impetus_y_root); + XConfigureWindow(vwm->display, win, CWX | CWY | CWBorderWidth, &changes); + break; + + case VWM_ADJUST_RESIZE: { + XWindowAttributes resized; + compute_resize((XEvent *)terminus, &resized); + /* move and resize the window @ resized */ + XDrawRectangle(vwm->display, VWM_XROOT(vwm), vwm->gc, clickety.lastrect.x, clickety.lastrect.y, clickety.lastrect.width, clickety.lastrect.height); + changes.x = resized.x; + changes.y = resized.y; + changes.width = resized.width; + changes.height = resized.height; + XConfigureWindow(vwm->display, win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &changes); + XUngrabServer(vwm->display); + break; + } + + default: + break; + } + /* once you manipulate the window it's no longer fullscreened, simply hitting Mod1+Return once will restore fullscreened mode */ + clickety.vwin->autoconfigured = VWM_WIN_AUTOCONF_NONE; + + clickety.vwin = NULL; /* reset clickety */ + + XFlush(vwm->display); + XUngrabPointer(vwm->display, CurrentTime); +} + + +/* on pointer buttonpress we initiate a clickety sequence; setup clickety with the window and impetus coordinates.. */ +int vwm_clickety_pressed(vwm_t *vwm, Window win, XButtonPressedEvent *impetus) +{ + vwm_window_t *vwin; + + /* verify the window still exists */ + if (!XGetWindowAttributes(vwm->display, win, &clickety.orig)) goto _fail; + + if (!(vwin = vwm_win_lookup(vwm, win))) goto _fail; + + if (impetus->state & WM_GRAB_MODIFIER) { + + /* always set the input focus to the clicked window, note if we allow this to happen on the root window, it enters sloppy focus mode + * until a non-root window is clicked, which is an interesting hybrid but not how I prefer it. */ + if (vwin != vwm->focused_desktop->focused_window && vwin->xwindow->id != VWM_XROOT(vwm)) { + vwm_win_focus(vwm, vwin); + vwm_win_mru(vwm, vwin); + } + + switch (impetus->button) { + case Button1: + /* immediately raise the window if we're relocating, + * resizes are supported without raising (which also enables NULL resizes to focus without raising) */ + clickety.mode = VWM_ADJUST_MOVE; + XRaiseWindow(vwm->display, win); + break; + + case Button3: + /* grab the server on resize for the xor rubber-banding's sake */ + XGrabServer(vwm->display); + XSync(vwm->display, False); + + /* FIXME: none of the resize DrawRectangle() calls consider the window border. */ + XDrawRectangle(vwm->display, VWM_XROOT(vwm), vwm->gc, clickety.orig.x, clickety.orig.y, clickety.orig.width, clickety.orig.height); + clickety.lastrect = clickety.orig; + + clickety.mode = VWM_ADJUST_RESIZE; + break; + + default: + goto _fail; + } + clickety.vwin = vwin; + clickety.impetus_x_root = impetus->x_root; + clickety.impetus_y_root = impetus->y_root; + clickety.impetus_x = impetus->x; + clickety.impetus_y = impetus->y; + } + + return 1; + +_fail: + XUngrabPointer(vwm->display, CurrentTime); + + return 0; +} |