change codename
[awesome.git] / mouse.c
blob6aa2f7f8e78b9e3fd44d4a935f6a85fed1699798
1 /*
2 * mouse.c - mouse managing
4 * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <math.h>
24 #include "common/tokenize.h"
25 #include "mouse.h"
26 #include "screen.h"
27 #include "tag.h"
28 #include "client.h"
29 #include "titlebar.h"
30 #include "layouts/floating.h"
31 #include "layouts/tile.h"
32 #include "layouts/magnifier.h"
34 #define MOUSEMASK (XCB_EVENT_MASK_BUTTON_PRESS \
35 | XCB_EVENT_MASK_BUTTON_RELEASE \
36 | XCB_EVENT_MASK_POINTER_MOTION)
38 extern awesome_t globalconf;
40 DO_LUA_NEW(static, button_t, mouse, "mouse", button_ref)
41 DO_LUA_GC(button_t, mouse, "mouse", button_unref)
42 DO_LUA_EQ(button_t, mouse, "mouse")
44 /** Define corners. */
45 typedef enum
47 AutoCorner,
48 TopRightCorner,
49 TopLeftCorner,
50 BottomLeftCorner,
51 BottomRightCorner
52 } corner_t;
54 /** Convert a corner name into a corner type.
55 * \param str A string.
56 * \param len String length.
57 * \return A corner type.
59 static corner_t
60 a_strtocorner(const char *str, size_t len)
62 switch (a_tokenize(str, len))
64 case A_TK_BOTTOMRIGHT: return BottomRightCorner;
65 case A_TK_BOTTOMLEFT: return BottomLeftCorner;
66 case A_TK_TOPLEFT: return TopLeftCorner;
67 case A_TK_TOPRIGHT: return TopRightCorner;
68 default: return AutoCorner;
72 /** Snap an area to the outside of an area.
73 * \param geometry geometry of the area to snap
74 * \param snap_geometry geometry of snapping area
75 * \param snap snap trigger in pixel
76 * \return snapped geometry
78 static area_t
79 mouse_snapclienttogeometry_outside(area_t geometry, area_t snap_geometry, int snap)
81 if(geometry.x < snap + snap_geometry.x + snap_geometry.width
82 && geometry.x > snap_geometry.x + snap_geometry.width)
83 geometry.x = snap_geometry.x + snap_geometry.width;
84 else if(geometry.x + geometry.width < snap_geometry.x
85 && geometry.x + geometry.width > snap_geometry.x - snap)
86 geometry.x = snap_geometry.x - geometry.width;
88 if(geometry.y < snap + snap_geometry.y + snap_geometry.height
89 && geometry.y > snap_geometry.y + snap_geometry.height)
90 geometry.y = snap_geometry.y + snap_geometry.height;
91 else if(geometry.y + geometry.height < snap_geometry.y
92 && geometry.y + geometry.height > snap_geometry.y - snap)
93 geometry.y = snap_geometry.y - geometry.height;
95 return geometry;
98 /** Snap an area to the inside of an area.
99 * \param geometry geometry of the area to snap
100 * \param snap_geometry geometry of snapping area
101 * \param snap snap trigger in pixel
102 * \return snapped geometry
104 static area_t
105 mouse_snapclienttogeometry_inside(area_t geometry, area_t snap_geometry, int snap)
107 if(abs(geometry.x) < snap + snap_geometry.x && geometry.x > snap_geometry.x)
108 geometry.x = snap_geometry.x;
109 else if(abs((snap_geometry.x + snap_geometry.width) - (geometry.x + geometry.width))
110 < snap)
111 geometry.x = snap_geometry.x + snap_geometry.width - geometry.width;
112 if(abs(geometry.y) < snap + snap_geometry.y && geometry.y > snap_geometry.y)
113 geometry.y = snap_geometry.y;
114 else if(abs((snap_geometry.y + snap_geometry.height) - (geometry.y + geometry.height))
115 < snap)
116 geometry.y = snap_geometry.y + snap_geometry.height - geometry.height;
118 return geometry;
121 /** Snap a client with a future geometry to the screen and other clients.
122 * \param c The client.
123 * \param geometry Geometry the client will get.
124 * \param snap The maximum distance in pixels to trigger a "snap".
125 * \return Geometry to set to the client.
127 static area_t
128 mouse_snapclient(client_t *c, area_t geometry, int snap)
130 client_t *snapper;
131 area_t snapper_geometry;
132 area_t screen_geometry =
133 screen_area_get(&globalconf.screens[c->screen].geometry,
134 globalconf.screens[c->screen].statusbar,
135 &globalconf.screens[c->screen].padding);
137 area_t screen_geometry_barless =
138 screen_area_get(&globalconf.screens[c->screen].geometry,
139 NULL,
140 &globalconf.screens[c->screen].padding);
142 geometry = titlebar_geometry_add(c->titlebar, c->border, geometry);
143 geometry.width += 2 * c->border;
144 geometry.height += 2 * c->border;
146 geometry =
147 mouse_snapclienttogeometry_inside(geometry, screen_geometry, snap);
149 geometry =
150 mouse_snapclienttogeometry_inside(geometry, screen_geometry_barless, snap);
152 for(snapper = globalconf.clients; snapper; snapper = snapper->next)
153 if(snapper != c && client_isvisible(snapper, c->screen))
155 snapper_geometry = snapper->geometry;
156 snapper_geometry.width += 2 * snapper->border;
157 snapper_geometry.height += 2 * snapper->border;
158 snapper_geometry = titlebar_geometry_add(snapper->titlebar, snapper->border, snapper_geometry);
159 geometry =
160 mouse_snapclienttogeometry_outside(geometry,
161 snapper_geometry,
162 snap);
165 geometry.width -= 2 * c->border;
166 geometry.height -= 2 * c->border;
167 return titlebar_geometry_remove(c->titlebar, c->border, geometry);
170 /** Set coordinates to a corner of an area.
172 * \param a The area.
173 * \param[in,out] x The x coordinate.
174 * \param[in,out] y The y coordinate.
175 * \param corner The corner to snap to.
176 * \return The corner the coordinates have been set to. If corner != AutoCorner
177 * this is always equal to \em corner.
179 * \todo rename/move this is still awkward and might be useful somewhere else.
181 static corner_t
182 mouse_snap_to_corner(area_t a, int *x, int *y, corner_t corner)
184 int top, bottom, left, right;
186 top = AREA_TOP(a);
187 bottom = AREA_BOTTOM(a);
188 left = AREA_LEFT(a);
189 right = AREA_RIGHT(a);
191 /* figure out the nearser corner */
192 if(corner == AutoCorner)
194 if(abs(top - *y) < abs(bottom - *y))
196 if(abs(left - *x) < abs(right - *x))
197 corner = TopLeftCorner;
198 else
199 corner = TopRightCorner;
201 else
203 if(abs(left - *x) < abs(right - *x))
204 corner = BottomLeftCorner;
205 else
206 corner = BottomRightCorner;
210 switch(corner)
212 case TopRightCorner:
213 *x = right;
214 *y = top;
215 break;
217 case TopLeftCorner:
218 *x = left;
219 *y = top;
220 break;
222 case BottomLeftCorner:
223 *x = left;
224 *y = bottom;
225 break;
227 case BottomRightCorner:
228 *x = right;
229 *y = bottom;
230 break;
232 default:
233 break;
236 return corner;
239 /** Redraw the infobox.
240 * \param ctx Draw context.
241 * \param sw The simple window.
242 * \param geometry The geometry to use for the box.
243 * \param border The client border size.
245 static void
246 mouse_infobox_draw(draw_context_t *ctx,
247 simple_window_t *sw,
248 area_t geometry, int border)
250 area_t draw_geometry = { 0, 0, ctx->width, ctx->height };
251 char size[64];
252 size_t len;
254 len = snprintf(size, sizeof(size), "<text align=\"center\"/>%dx%d+%d+%d",
255 geometry.width, geometry.height, geometry.x, geometry.y);
256 draw_rectangle(ctx, draw_geometry, 1.0, true, &globalconf.colors.bg);
257 draw_text(ctx, globalconf.font, draw_geometry, size, len, NULL);
258 simplewindow_move(sw,
259 geometry.x + ((2 * border + geometry.width) - sw->geometry.width) / 2,
260 geometry.y + ((2 * border + geometry.height) - sw->geometry.height) / 2);
261 simplewindow_refresh_pixmap(sw);
262 xcb_aux_sync(ctx->connection);
265 #define MOUSE_INFOBOX_STRING_DEFAULT "0000x0000+0000+0000"
267 /** Initialize the infobox window.
268 * \param phys_screen Physical screen number.
269 * \param border Border size of the client.
270 * \param geometry Client geometry.
271 * \param ctx Draw context to create.
272 * \return The simple window.
274 static simple_window_t *
275 mouse_infobox_new(int phys_screen, int border, area_t geometry,
276 draw_context_t **ctx)
278 simple_window_t *sw;
279 area_t geom;
280 draw_parser_data_t pdata;
282 draw_parser_data_init(&pdata);
284 geom = draw_text_extents(globalconf.connection,
285 globalconf.default_screen,
286 globalconf.font,
287 MOUSE_INFOBOX_STRING_DEFAULT,
288 sizeof(MOUSE_INFOBOX_STRING_DEFAULT)-1,
289 &pdata);
290 geom.x = geometry.x + ((2 * border + geometry.width) - geom.width) / 2;
291 geom.y = geometry.y + ((2 * border + geometry.height) - geom.height) / 2;
293 sw = simplewindow_new(globalconf.connection, phys_screen,
294 geom.x, geom.y,
295 geom.width, geom.height, 0);
297 *ctx = draw_context_new(globalconf.connection, sw->phys_screen,
298 sw->geometry.width, sw->geometry.height,
299 sw->pixmap,
300 &globalconf.colors.fg,
301 &globalconf.colors.bg);
303 xcb_map_window(globalconf.connection, sw->window);
304 mouse_infobox_draw(*ctx, sw, geometry, border);
306 draw_parser_data_wipe(&pdata);
308 return sw;
311 /** Get the pointer position.
312 * \param window The window to get position on.
313 * \param x will be set to the Pointer-x-coordinate relative to window
314 * \param y will be set to the Pointer-y-coordinate relative to window
315 * \param mask will be set to the current buttons state
316 * \return true on success, false if an error occured
318 static bool
319 mouse_query_pointer(xcb_window_t window, int *x, int *y, uint16_t *mask)
321 xcb_query_pointer_cookie_t query_ptr_c;
322 xcb_query_pointer_reply_t *query_ptr_r;
324 query_ptr_c = xcb_query_pointer_unchecked(globalconf.connection, window);
325 query_ptr_r = xcb_query_pointer_reply(globalconf.connection, query_ptr_c, NULL);
327 if(!query_ptr_r || !query_ptr_r->same_screen)
328 return false;
330 *x = query_ptr_r->win_x;
331 *y = query_ptr_r->win_y;
332 if (mask)
333 *mask = query_ptr_r->mask;
335 p_delete(&query_ptr_r);
337 return true;
340 /** Get the pointer position on the screen.
341 * \param screen This will be set to the screen number the mouse is on.
342 * \param x This will be set to the Pointer-x-coordinate relative to window.
343 * \param y This will be set to the Pointer-y-coordinate relative to window.
344 * \param mask This will be set to the current buttons state.
345 * \return True on success, false if an error occured.
347 static bool
348 mouse_query_pointer_root(int *s, int *x, int *y, uint16_t *mask)
350 for(int screen = 0;
351 screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
352 screen++)
354 xcb_window_t root = xutil_screen_get(globalconf.connection, screen)->root;
356 if(mouse_query_pointer(root, x, y, mask))
358 *s = screen;
359 return true;
362 return false;
365 /** Grab the Pointer.
366 * \param window The window grabbed.
367 * \param cursor The cursor to display (see struct.h CurNormal, CurResize, etc).
368 * \return True on success, false if an error occured.
370 static bool
371 mouse_grab_pointer(xcb_window_t window, size_t cursor)
373 xcb_grab_pointer_cookie_t grab_ptr_c;
374 xcb_grab_pointer_reply_t *grab_ptr_r;
376 if(cursor >= CurLast)
377 cursor = CurNormal;
379 grab_ptr_c = xcb_grab_pointer_unchecked(globalconf.connection, false, window,
380 MOUSEMASK, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
381 window, globalconf.cursor[cursor], XCB_CURRENT_TIME);
382 grab_ptr_r = xcb_grab_pointer_reply(globalconf.connection, grab_ptr_c, NULL);
384 if(!grab_ptr_r)
385 return false;
387 p_delete(&grab_ptr_r);
389 return true;
392 /** Ungrab the Pointer
394 static inline void
395 mouse_ungrab_pointer(void)
397 xcb_ungrab_pointer(globalconf.connection, XCB_CURRENT_TIME);
400 /** Set the pointer position.
401 * \param window The destination window.
402 * \param x X-coordinate inside window.
403 * \param y Y-coordinate inside window.
405 static inline void
406 mouse_warp_pointer(xcb_window_t window, int x, int y)
408 xcb_warp_pointer(globalconf.connection, XCB_NONE, window,
409 0, 0, 0, 0, x, y );
412 /** Utility function to help with mouse-dragging
414 * \param x set to x-coordinate of the last event on return
415 * \param y set to y-coordinate of the last event on return
416 * \return true if an motion event was received
417 * false if an button release event was received
419 static bool
420 mouse_track_mouse_drag(int *x, int *y)
422 xcb_generic_event_t *ev;
423 xcb_motion_notify_event_t *ev_motion;
424 xcb_button_release_event_t *ev_button;
426 while(true)
428 while((ev = xcb_wait_for_event(globalconf.connection)))
430 switch((ev->response_type & 0x7F))
433 case XCB_MOTION_NOTIFY:
434 ev_motion = (xcb_motion_notify_event_t*) ev;
435 *x = ev_motion->event_x;
436 *y = ev_motion->event_y;
437 p_delete(&ev);
438 return true;
440 case XCB_BUTTON_RELEASE:
441 ev_button = (xcb_button_release_event_t*) ev;
442 *x = ev_button->event_x;
443 *y = ev_button->event_y;
444 p_delete(&ev);
445 return false;
447 default:
448 xcb_handle_event(globalconf.evenths, ev);
449 p_delete(&ev);
450 break;
457 /** Get the client that contains the pointer.
459 * \return The client that contains the pointer or NULL.
461 static client_t *
462 mouse_get_client_under_pointer(int phys_screen)
464 xcb_window_t root;
465 xcb_query_pointer_cookie_t query_ptr_c;
466 xcb_query_pointer_reply_t *query_ptr_r;
467 client_t *c = NULL;
469 root = xutil_screen_get(globalconf.connection, phys_screen)->root;
471 query_ptr_c = xcb_query_pointer_unchecked(globalconf.connection, root);
472 query_ptr_r = xcb_query_pointer_reply(globalconf.connection, query_ptr_c, NULL);
474 if(query_ptr_r)
476 c = client_getbywin(query_ptr_r->child);
477 p_delete(&query_ptr_r);
480 return c;
483 /** Move the focused window with the mouse.
484 * \param c The client.
485 * \param snap The maximum distance in pixels to trigger a "snap".
486 * \param infobox Enable or disable the infobox.
488 static void
489 mouse_client_move(client_t *c, int snap, bool infobox)
491 /* current mouse postion */
492 int mouse_x, mouse_y;
493 /* last mouse position */
494 int last_x = 0, last_y = 0;
495 /* current layout */
496 layout_t *layout;
497 /* the infobox */
498 simple_window_t *sw = NULL;
499 draw_context_t *ctx;
500 /* the root window */
501 xcb_window_t root;
503 layout = layout_get_current(c->screen);
504 root = xutil_screen_get(globalconf.connection, c->phys_screen)->root;
506 /* get current pointer position */
507 mouse_query_pointer(root, &last_x, &last_y, NULL);
509 /* grab pointer */
510 if(!mouse_grab_pointer(root, CurMove))
511 return;
513 c->ismax = false;
515 if(infobox && (c->isfloating || layout == layout_floating))
516 sw = mouse_infobox_new(c->phys_screen, c->border, c->geometry, &ctx);
518 /* for each motion event */
519 while(mouse_track_mouse_drag(&mouse_x, &mouse_y))
520 if(c->isfloating || layout == layout_floating)
522 area_t geometry;
524 /* calc new geometry */
525 geometry = c->geometry;
526 geometry.x += (mouse_x - last_x);
527 geometry.y += (mouse_y - last_y);
529 /* snap and move */
530 geometry = mouse_snapclient(c, geometry, snap);
531 c->ismoving = true;
532 client_resize(c, geometry, false);
533 c->ismoving = false;
535 /* draw the infobox */
536 if(sw)
537 mouse_infobox_draw(ctx, sw, c->geometry, c->border);
539 /* keep track */
540 last_x = mouse_x;
541 last_y = mouse_y;
543 else
545 int newscreen;
546 client_t *target;
548 /* client moved to another screen? */
549 newscreen = screen_get_bycoord(globalconf.screens_info, c->screen,
550 mouse_x, mouse_y);
551 if(newscreen != c->screen)
553 screen_client_moveto(c, newscreen, true);
554 globalconf.screens[c->screen].need_arrange = true;
555 globalconf.screens[newscreen].need_arrange = true;
556 layout_refresh();
557 titlebar_refresh();
560 /* find client to swap with */
561 target = mouse_get_client_under_pointer(c->phys_screen);
563 /* swap position */
564 if(target && target != c && !target->isfloating)
566 client_list_swap(&globalconf.clients, c, target);
567 globalconf.screens[c->screen].need_arrange = true;
568 layout_refresh();
569 titlebar_refresh();
573 /* ungrab pointer */
574 xcb_ungrab_pointer(globalconf.connection, XCB_CURRENT_TIME);
576 /* free the infobox */
577 if(sw)
579 draw_context_delete(&ctx);
580 simplewindow_delete(&sw);
585 /** Resize a floating client with the mouse.
586 * \param c The client to resize.
587 * \param corner The corner to resize with.
588 * \param infobox Enable or disable the infobox.
590 static void
591 mouse_client_resize_floating(client_t *c, corner_t corner, bool infobox)
593 xcb_screen_t *screen;
594 /* one corner of the client has a fixed position */
595 int fixed_x, fixed_y;
596 /* the other is moved with the mouse */
597 int mouse_x = 0, mouse_y = 0;
598 /* the infobox */
599 simple_window_t *sw = NULL;
600 draw_context_t *ctx;
601 size_t cursor = CurResize;
602 int top, bottom, left, right;
604 screen = xutil_screen_get(globalconf.connection, c->phys_screen);
606 /* get current mouse position */
607 mouse_query_pointer(screen->root, &mouse_x, &mouse_y, NULL);
609 top = c->geometry.y;
610 bottom = top + c->geometry.height;
611 left = c->geometry.x;
612 right = left + c->geometry.width;
614 /* figure out which corner to move */
615 corner = mouse_snap_to_corner(c->geometry, &mouse_x, &mouse_y, corner);
617 /* the opposite corner is fixed */
618 fixed_x = (mouse_x == left) ? right : left;
619 fixed_y = (mouse_y == top) ? bottom : top;
621 /* select cursor */
622 switch(corner)
624 default:
625 cursor = CurTopLeft;
626 break;
627 case TopRightCorner:
628 cursor = CurTopRight;
629 break;
630 case BottomLeftCorner:
631 cursor = CurBotLeft;
632 break;
633 case BottomRightCorner:
634 cursor = CurBotRight;
635 break;
638 /* grab the pointer */
639 if(!mouse_grab_pointer(screen->root, cursor))
640 return;
642 /* set pointer to the moveable corner */
643 mouse_warp_pointer(screen->root, mouse_x, mouse_y);
645 /* create the infobox */
646 if(infobox)
647 sw = mouse_infobox_new(c->phys_screen, c->border, c->geometry, &ctx);
649 /* for each motion event */
650 while(mouse_track_mouse_drag(&mouse_x, &mouse_y))
652 /* new client geometry */
653 area_t geo = { .x = MIN(fixed_x, mouse_x),
654 .y = MIN(fixed_y, mouse_y),
655 .width = (MAX(fixed_x, mouse_x) - MIN(fixed_x, mouse_x)),
656 .height = (MAX(fixed_y, mouse_y) - MIN(fixed_y, mouse_y)) };
657 /* new moveable corner */
658 corner_t new_corner;
660 if(mouse_x == AREA_LEFT(geo))
661 new_corner = (mouse_y == AREA_TOP(geo)) ? TopLeftCorner : BottomLeftCorner;
662 else
663 new_corner = (mouse_y == AREA_TOP(geo)) ? TopRightCorner : BottomRightCorner;
665 /* update cursor */
666 if(corner != new_corner)
668 corner = new_corner;
670 switch(corner)
672 default: cursor = CurTopLeft; break;
673 case TopRightCorner: cursor = CurTopRight; break;
674 case BottomLeftCorner: cursor = CurBotLeft; break;
675 case BottomRightCorner: cursor = CurBotRight; break;
678 xcb_change_active_pointer_grab(globalconf.connection, globalconf.cursor[cursor],
679 XCB_CURRENT_TIME, MOUSEMASK);
682 if(c->hassizehints && c->honorsizehints)
684 int dx, dy;
686 /* apply size hints */
687 geo = client_geometry_hints(c, geo);
689 /* get the nonmoveable corner back onto fixed_x,fixed_y */
690 switch(corner)
692 default /* TopLeftCorner */:
693 dx = fixed_x - AREA_RIGHT(geo);
694 dy = fixed_y - AREA_BOTTOM(geo);
695 break;
696 case TopRightCorner:
697 dx = fixed_x - AREA_LEFT(geo);
698 dy = fixed_y - AREA_BOTTOM(geo);
699 break;
700 case BottomRightCorner:
701 dx = fixed_x - AREA_LEFT(geo);
702 dy = fixed_y - AREA_TOP(geo);
703 break;
704 case BottomLeftCorner:
705 dx = fixed_x - AREA_RIGHT(geo);
706 dy = fixed_y - AREA_TOP(geo);
707 break;
710 geo.x += dx;
711 geo.y += dy;
714 /* resize the client */
715 client_resize(c, geo, false);
716 /* redaw titlebar live */
717 titlebar_draw(c);
719 /* draw the infobox */
720 if(sw)
721 mouse_infobox_draw(ctx, sw, c->geometry, c->border);
724 /* relase pointer */
725 mouse_ungrab_pointer();
727 /* free the infobox */
728 if(sw)
730 draw_context_delete(&ctx);
731 simplewindow_delete(&sw);
735 /** Resize the master column/row of a tiled layout
736 * \param c A client on the tag/layout to resize.
738 static void
739 mouse_client_resize_tiled(client_t *c)
741 xcb_screen_t *screen;
742 /* screen area modulo statusbar */
743 area_t area;
744 /* current tag */
745 tag_t *tag;
746 /* current layout */
747 layout_t *layout;
749 int mouse_x = 0, mouse_y = 0;
750 size_t cursor = CurResize;
752 screen = xutil_screen_get(globalconf.connection, c->phys_screen);
753 tag = tags_get_current(c->screen)[0];
754 layout = tag->layout;
756 area = screen_area_get(&globalconf.screens[tag->screen].geometry,
757 globalconf.screens[tag->screen].statusbar,
758 &globalconf.screens[tag->screen].padding);
760 mouse_query_pointer(screen->root, &mouse_x, &mouse_y, NULL);
762 /* select initial pointer position */
763 if(layout == layout_tile)
765 mouse_x = area.x + area.width * tag->mwfact;
766 cursor = CurResizeH;
768 else if(layout == layout_tileleft)
770 mouse_x = area.x + area.width * (1. - tag->mwfact);
771 cursor = CurResizeH;
773 else if(layout == layout_tilebottom)
775 mouse_y = area.y + area.height * tag->mwfact;
776 cursor = CurResizeV;
778 else if(layout == layout_tiletop)
780 mouse_y = area.y + area.height * (1. - tag->mwfact);
781 cursor = CurResizeV;
783 else
784 return;
786 /* grab the pointer */
787 if(!mouse_grab_pointer(screen->root, cursor))
788 return;
790 /* set pointer to the moveable border */
791 mouse_warp_pointer(screen->root, mouse_x, mouse_y);
793 xcb_aux_sync(globalconf.connection);
795 /* for each motion event */
796 while(mouse_track_mouse_drag(&mouse_x, &mouse_y))
798 double mwfact = 0, fact_x, fact_y;
800 /* calculate new master / rest ratio */
801 fact_x = (double) (mouse_x - area.x) / area.width;
802 fact_y = (double) (mouse_y - area.y) / area.height;
804 if(layout == layout_tile)
805 mwfact = fact_x;
806 else if(layout == layout_tileleft)
807 mwfact = 1. - fact_x;
808 else if(layout == layout_tilebottom)
809 mwfact = fact_y;
810 else if(layout == layout_tiletop)
811 mwfact = 1. - fact_y;
813 /* keep mwfact within reasonable bounds */
814 mwfact = MIN(MAX( 0.01, mwfact), 0.99);
816 /* refresh layout */
817 if(fabs(tag->mwfact - mwfact) >= 0.01)
819 tag->mwfact = mwfact;
820 globalconf.screens[tag->screen].need_arrange = true;
821 layout_refresh();
822 titlebar_refresh();
826 /* relase pointer */
827 mouse_ungrab_pointer();
830 /** Resize the master client in mangifier layout
831 * \param c The client to resize.
832 * \param infobox Enable or disable the infobox.
834 static void
835 mouse_client_resize_magnified(client_t *c, bool infobox)
837 /* screen area modulo statusbar */
838 area_t area;
839 /* center of area */
840 int center_x, center_y;
841 /* max. distance from the center */
842 double maxdist;
843 /* mouse position */
844 int mouse_x = 0, mouse_y = 0;
845 /* cursor while grabbing */
846 size_t cursor = CurResize;
847 corner_t corner = AutoCorner;
848 /* current tag */
849 tag_t *tag;
850 /* the infobox */
851 simple_window_t *sw = NULL;
852 draw_context_t *ctx;
853 xcb_window_t root;
855 tag = tags_get_current(c->screen)[0];
857 root = xutil_screen_get(globalconf.connection, c->phys_screen)->root;
859 area = screen_area_get(&globalconf.screens[tag->screen].geometry,
860 globalconf.screens[tag->screen].statusbar,
861 &globalconf.screens[tag->screen].padding);
863 center_x = area.x + (round(area.width / 2.));
864 center_y = area.y + (round(area.height / 2.));
866 maxdist = round(sqrt((area.width*area.width) + (area.height*area.height)) / 2.);
868 root = xutil_screen_get(globalconf.connection, c->phys_screen)->root;
870 if(!mouse_query_pointer(root, &mouse_x, &mouse_y, NULL))
871 return;
873 /* select corner */
874 corner = mouse_snap_to_corner(c->geometry, &mouse_x, &mouse_y, corner);
876 /* select cursor */
877 switch(corner)
879 default:
880 cursor = CurTopLeft;
881 break;
882 case TopRightCorner:
883 cursor = CurTopRight;
884 break;
885 case BottomLeftCorner:
886 cursor = CurBotLeft;
887 break;
888 case BottomRightCorner:
889 cursor = CurBotRight;
890 break;
893 /* grab pointer */
894 if(!mouse_grab_pointer(root, cursor))
895 return;
897 /* move pointer to corner */
898 mouse_warp_pointer(root, mouse_x, mouse_y);
900 /* create the infobox */
901 if(infobox)
902 sw = mouse_infobox_new(c->phys_screen, c->border, c->geometry, &ctx);
904 /* for each motion event */
905 while(mouse_track_mouse_drag(&mouse_x, &mouse_y))
907 /* \todo keep pointer on screen diagonals */
908 double mwfact, dist, dx, dy;
910 /* calc distance from the center */
911 dx = center_x - mouse_x;
912 dy = center_y - mouse_y;
913 dist = sqrt((dx*dx) + (dy*dy));
915 /* new master/rest ratio */
916 mwfact = dist / maxdist;
918 /* keep mwfact within reasonable bounds */
919 mwfact = MIN(MAX( 0.01, mwfact), 0.99);
921 /* refresh the layout */
922 if(fabs(tag->mwfact - mwfact) >= 0.01)
924 tag->mwfact = mwfact;
925 globalconf.screens[tag->screen].need_arrange = true;
926 layout_refresh();
927 titlebar_refresh();
930 /* draw the infobox */
931 if(sw)
932 mouse_infobox_draw(ctx, sw, c->geometry, c->border);
935 /* ungrab pointer */
936 mouse_ungrab_pointer();
938 /* free the infobox */
939 if(sw)
941 draw_context_delete(&ctx);
942 simplewindow_delete(&sw);
946 /** Resize a client with the mouse.
947 * \param c The client to resize.
948 * \param corner The corner to use.
949 * \param infobox Enable or disable the info box.
951 static void
952 mouse_client_resize(client_t *c, corner_t corner, bool infobox)
954 int n, screen;
955 tag_t **curtags;
956 layout_t *layout;
957 xcb_screen_t *s;
959 curtags = tags_get_current(c->screen);
960 layout = curtags[0]->layout;
961 s = xutil_screen_get(globalconf.connection, c->phys_screen);
963 /* only handle floating, tiled and magnifier layouts */
964 if(layout == layout_floating || c->isfloating)
966 if(c->isfixed)
967 goto bailout;
969 c->ismax = false;
971 mouse_client_resize_floating(c, corner, infobox);
973 else if(layout == layout_tile || layout == layout_tileleft
974 || layout == layout_tilebottom || layout == layout_tiletop)
976 screen = c->screen;
977 for(n = 0, c = globalconf.clients; c; c = c->next)
978 if(IS_TILED(c, screen))
979 n++;
981 /* only masters on this screen? */
982 if(n <= curtags[0]->nmaster)
983 goto bailout;
985 /* no tiled clients on this screen? */
986 for(c = globalconf.clients; c && !IS_TILED(c, screen); c = c->next);
987 if(!c)
988 goto bailout;
990 mouse_client_resize_tiled(c);
992 else if(layout == layout_magnifier)
993 mouse_client_resize_magnified(c, infobox);
995 bailout:
996 p_delete(&curtags);
999 /** Resize a client with mouse.
1000 * \param L The Lua VM state.
1002 * \luastack
1003 * \lvalue A client.
1004 * \lparam An optional table with keys: `corner', such as bottomleft,
1005 * topright, etc, to specify which corner to grab (default to auto) and
1006 * `infobox' to enable or disable the coordinates and dimensions box (default to
1007 * enabled).
1010 luaA_client_mouse_resize(lua_State *L)
1012 client_t **c = luaA_checkudata(L, 1, "client");
1013 corner_t corner = AutoCorner;
1014 bool infobox = true;
1015 size_t len;
1016 const char *buf;
1018 if(lua_gettop(L) == 2 && !lua_isnil(L, 2))
1020 luaA_checktable(L, 2);
1021 buf = luaA_getopt_lstring(L, 2, "corner", "auto", &len);
1022 corner = a_strtocorner(buf, len);
1023 infobox = luaA_getopt_boolean(L, 2, "infobox", true);
1026 mouse_client_resize(*c, corner, infobox);
1028 return 0;
1031 /** Move a client with mouse.
1032 * \param L The Lua VM state.
1034 * \luastack
1035 * \lvalue A client.
1036 * \lparam An optional table with keys: `snap' for pixel to snap (default to 8), and
1037 * `infobox' to enable or disable the coordinates and dimensions box (default to
1038 * enabled).
1041 luaA_client_mouse_move(lua_State *L)
1043 client_t **c = luaA_checkudata(L, 1, "client");
1044 int snap = 8;
1045 bool infobox = true;
1047 if(lua_gettop(L) == 2 && !lua_isnil(L, 2))
1049 luaA_checktable(L, 2);
1050 snap = luaA_getopt_number(L, 2, "snap", 8);
1051 infobox = luaA_getopt_boolean(L, 2, "infobox", true);
1054 mouse_client_move(*c, snap, infobox);
1056 return 0;
1059 /** Create a new mouse button bindings.
1060 * \param L The Lua VM state.
1061 * \return The number of elements pushed on stack.
1062 * \luastack
1063 * \lparam A table with modifiers keys.
1064 * \lparam A mouse button number.
1065 * \lparam A function to execute on click events.
1066 * \lreturn A mouse button binding.
1068 static int
1069 luaA_mouse_new(lua_State *L)
1071 int i, len;
1072 button_t *button;
1074 luaA_checktable(L, 2);
1075 /* arg 3 is mouse button */
1076 i = luaL_checknumber(L, 3);
1077 /* arg 4 is cmd to run */
1078 luaA_checkfunction(L, 4);
1080 button = p_new(button_t, 1);
1081 button->button = xutil_button_fromint(i);
1082 button->fct = luaL_ref(L, LUA_REGISTRYINDEX);
1084 len = lua_objlen(L, 2);
1085 for(i = 1; i <= len; i++)
1087 lua_rawgeti(L, 2, i);
1088 button->mod |= xutil_key_mask_fromstr(luaL_checkstring(L, -1));
1091 return luaA_mouse_userdata_new(L, button);
1094 /** Mouse object.
1095 * \param L The Lua VM state.
1096 * \return The number of elements pushed on stack.
1097 * \luastack
1098 * \lfield coords Mouse coordinates.
1099 * \lfield screen Mouse screen number.
1101 static int
1102 luaA_mouse_index(lua_State *L)
1104 size_t len;
1105 const char *attr = luaL_checklstring(L, 2, &len);
1106 int mouse_x, mouse_y, i;
1107 int screen;
1109 switch(a_tokenize(attr, len))
1111 case A_TK_SCREEN:
1112 if(!mouse_query_pointer_root(&screen, &mouse_x, &mouse_y, NULL))
1113 return 0;
1115 i = screen_get_bycoord(globalconf.screens_info, screen, mouse_x, mouse_y);
1117 lua_pushnumber(L, i + 1);
1118 break;
1119 default:
1120 return 0;
1123 return 1;
1126 /** Newindex for mouse.
1127 * \param L The Lua VM state.
1128 * \return The number of elements pushed on stack.
1130 static int
1131 luaA_mouse_newindex(lua_State *L)
1133 size_t len;
1134 const char *attr = luaL_checklstring(L, 2, &len);
1135 int x, y = 0;
1136 xcb_window_t root;
1137 int screen, phys_screen;
1139 switch(a_tokenize(attr, len))
1141 case A_TK_SCREEN:
1142 screen = luaL_checknumber(L, 3) - 1;
1143 luaA_checkscreen(screen);
1145 /* we need the physical one to get the root window */
1146 phys_screen = screen_virttophys(screen);
1147 root = xutil_screen_get(globalconf.connection, phys_screen)->root;
1149 x = globalconf.screens_info->geometry[screen].x;
1150 y = globalconf.screens_info->geometry[screen].y;
1152 mouse_warp_pointer(root, x, y);
1153 break;
1154 default:
1155 return 0;
1158 return 0;
1161 /** Get or set the mouse coords.
1162 * \param L The Lua VM state.
1163 * \return The number of elements pushed on stack.
1164 * \luastack
1165 * \lparam None or a table with x and y keys as mouse coordinates.
1166 * \lreturn A table with mouse coordinates.
1168 static int
1169 luaA_mouse_coords(lua_State *L)
1171 uint16_t mask;
1172 int screen, x, y, mouse_x, mouse_y, i = 0;
1174 if(lua_gettop(L) == 1)
1176 xcb_window_t root;
1178 luaA_checktable(L, 1);
1180 if(!mouse_query_pointer_root(&screen, &mouse_x, &mouse_y, &mask))
1181 return 0;
1183 x = luaA_getopt_number(L, 1, "x", mouse_x);
1184 y = luaA_getopt_number(L, 1, "y", mouse_y);
1186 root = xutil_screen_get(globalconf.connection, screen)->root;
1187 mouse_warp_pointer(root, x, y);
1188 lua_pop(L, 1);
1191 if(!mouse_query_pointer_root(&screen, &mouse_x, &mouse_y, &mask))
1192 return 0;
1194 lua_newtable(L);
1195 lua_pushnumber(L, mouse_x);
1196 lua_setfield(L, -2, "x");
1197 lua_pushnumber(L, mouse_y);
1198 lua_setfield(L, -2, "y");
1199 lua_newtable(L);
1200 if (mask & XCB_BUTTON_MASK_1)
1202 lua_pushnumber(L, 1);
1203 lua_rawseti(L, -2, ++i);
1205 if (mask & XCB_BUTTON_MASK_2)
1207 lua_pushnumber(L, 2);
1208 lua_rawseti(L, -2, ++i);
1210 if (mask & XCB_BUTTON_MASK_3)
1212 lua_pushnumber(L, 3);
1213 lua_rawseti(L, -2, ++i);
1215 if (mask & XCB_BUTTON_MASK_4)
1217 lua_pushnumber(L, 4);
1218 lua_rawseti(L, -2, ++i);
1220 if (mask & XCB_BUTTON_MASK_5)
1222 lua_pushnumber(L, 5);
1223 lua_rawseti(L, -2, ++i);
1225 lua_setfield(L, -2, "buttons");
1227 return 1;
1230 const struct luaL_reg awesome_mouse_methods[] =
1232 { "__call", luaA_mouse_new },
1233 { "__index", luaA_mouse_index },
1234 { "__newindex", luaA_mouse_newindex },
1235 { "coords", luaA_mouse_coords },
1236 { NULL, NULL }
1238 const struct luaL_reg awesome_mouse_meta[] =
1240 { "__gc", luaA_mouse_gc },
1241 { "__eq", luaA_mouse_eq },
1242 { NULL, NULL }
1245 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80