mouse: remove client_move, move it to awful
[awesome.git] / mouse.c
blob45e6a59c005115013ebbf720c9ee5e64173ccd8e
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 "screen.h"
26 #include "tag.h"
27 #include "titlebar.h"
28 #include "layouts/floating.h"
29 #include "layouts/tile.h"
30 #include "layouts/magnifier.h"
31 #include "common/xcursor.h"
33 #define MOUSEMASK (XCB_EVENT_MASK_BUTTON_PRESS \
34 | XCB_EVENT_MASK_BUTTON_RELEASE \
35 | XCB_EVENT_MASK_POINTER_MOTION)
37 extern awesome_t globalconf;
39 DO_LUA_NEW(extern, button_t, button, "button", button_ref)
40 DO_LUA_GC(button_t, button, "button", button_unref)
41 DO_LUA_EQ(button_t, button, "button")
43 /** Define corners. */
44 typedef enum
46 AutoCorner,
47 TopRightCorner,
48 TopLeftCorner,
49 BottomLeftCorner,
50 BottomRightCorner
51 } corner_t;
53 /** Convert a corner name into a corner type.
54 * \param str A string.
55 * \param len String length.
56 * \return A corner type.
58 static corner_t
59 a_strtocorner(const char *str, size_t len)
61 switch (a_tokenize(str, len))
63 case A_TK_BOTTOMRIGHT: return BottomRightCorner;
64 case A_TK_BOTTOMLEFT: return BottomLeftCorner;
65 case A_TK_TOPLEFT: return TopLeftCorner;
66 case A_TK_TOPRIGHT: return TopRightCorner;
67 default: return AutoCorner;
71 /** Delete a button.
72 * \param button The button to destroy.
74 void
75 button_delete(button_t **button)
77 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*button)->press);
78 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, (*button)->release);
79 p_delete(button);
82 /** Set coordinates to a corner of an area.
84 * \param a The area.
85 * \param[in,out] x The x coordinate.
86 * \param[in,out] y The y coordinate.
87 * \param corner The corner to snap to.
88 * \return The corner the coordinates have been set to. If corner != AutoCorner
89 * this is always equal to \em corner.
91 * \todo rename/move this is still awkward and might be useful somewhere else.
93 static corner_t
94 mouse_snap_to_corner(area_t a, int *x, int *y, corner_t corner)
96 int top, bottom, left, right;
98 top = AREA_TOP(a);
99 bottom = AREA_BOTTOM(a);
100 left = AREA_LEFT(a);
101 right = AREA_RIGHT(a);
103 /* figure out the nearser corner */
104 if(corner == AutoCorner)
106 if(abs(top - *y) < abs(bottom - *y))
108 if(abs(left - *x) < abs(right - *x))
109 corner = TopLeftCorner;
110 else
111 corner = TopRightCorner;
113 else
115 if(abs(left - *x) < abs(right - *x))
116 corner = BottomLeftCorner;
117 else
118 corner = BottomRightCorner;
122 switch(corner)
124 case TopRightCorner:
125 *x = right;
126 *y = top;
127 break;
129 case TopLeftCorner:
130 *x = left;
131 *y = top;
132 break;
134 case BottomLeftCorner:
135 *x = left;
136 *y = bottom;
137 break;
139 case BottomRightCorner:
140 *x = right;
141 *y = bottom;
142 break;
144 default:
145 break;
148 return corner;
151 /** Redraw the infobox.
152 * \param sw The simple window.
153 * \param geometry The geometry to use for the box.
154 * \param border The client border size.
156 static void
157 mouse_infobox_draw(simple_window_t *sw, area_t geometry, int border)
159 area_t draw_geometry = { 0, 0, sw->ctx.width, sw->ctx.height };
160 char size[64];
161 size_t len;
162 xcolor_t color_without_alpha = globalconf.colors.bg;
163 color_without_alpha.alpha = 0xffff;
165 len = snprintf(size, sizeof(size), "<text align=\"center\"/>%dx%d+%d+%d",
166 geometry.width, geometry.height, geometry.x, geometry.y);
167 draw_rectangle(&sw->ctx, draw_geometry, 1.0, true, &color_without_alpha);
168 draw_text(&sw->ctx, globalconf.font, PANGO_ELLIPSIZE_NONE, PANGO_WRAP_WORD, draw_geometry, size, len, NULL);
169 simplewindow_move(sw,
170 geometry.x + ((2 * border + geometry.width) - sw->geometry.width) / 2,
171 geometry.y + ((2 * border + geometry.height) - sw->geometry.height) / 2);
172 simplewindow_refresh_pixmap(sw);
173 xcb_flush(globalconf.connection);
176 #define MOUSE_INFOBOX_STRING_DEFAULT "0000x0000+0000+0000"
178 /** Initialize the infobox window.
179 * \param sw The simple window to init.
180 * \param phys_screen Physical screen number.
181 * \param border Border size of the client.
182 * \param geometry Client geometry.
183 * \return The simple window.
185 static void
186 mouse_infobox_new(simple_window_t *sw, int phys_screen, int border, area_t geometry)
188 area_t geom;
189 draw_parser_data_t pdata;
191 draw_parser_data_init(&pdata);
193 geom = draw_text_extents(globalconf.font,
194 MOUSE_INFOBOX_STRING_DEFAULT,
195 sizeof(MOUSE_INFOBOX_STRING_DEFAULT)-1,
196 &pdata);
197 geom.x = geometry.x + ((2 * border + geometry.width) - geom.width) / 2;
198 geom.y = geometry.y + ((2 * border + geometry.height) - geom.height) / 2;
200 p_clear(sw, 1);
201 simplewindow_init(sw, phys_screen, geom, 0, East,
202 &globalconf.colors.fg, &globalconf.colors.bg);
204 xcb_map_window(globalconf.connection, sw->window);
205 mouse_infobox_draw(sw, geometry, border);
207 draw_parser_data_wipe(&pdata);
210 /** Get the pointer position.
211 * \param window The window to get position on.
212 * \param x will be set to the Pointer-x-coordinate relative to window
213 * \param y will be set to the Pointer-y-coordinate relative to window
214 * \param mask will be set to the current buttons state
215 * \return true on success, false if an error occured
217 bool
218 mouse_query_pointer(xcb_window_t window, int *x, int *y, uint16_t *mask)
220 xcb_query_pointer_cookie_t query_ptr_c;
221 xcb_query_pointer_reply_t *query_ptr_r;
223 query_ptr_c = xcb_query_pointer_unchecked(globalconf.connection, window);
224 query_ptr_r = xcb_query_pointer_reply(globalconf.connection, query_ptr_c, NULL);
226 if(!query_ptr_r || !query_ptr_r->same_screen)
227 return false;
229 *x = query_ptr_r->win_x;
230 *y = query_ptr_r->win_y;
231 if (mask)
232 *mask = query_ptr_r->mask;
234 p_delete(&query_ptr_r);
236 return true;
239 /** Get the pointer position on the screen.
240 * \param screen This will be set to the screen number the mouse is on.
241 * \param x This will be set to the Pointer-x-coordinate relative to window.
242 * \param y This will be set to the Pointer-y-coordinate relative to window.
243 * \param mask This will be set to the current buttons state.
244 * \return True on success, false if an error occured.
246 static bool
247 mouse_query_pointer_root(int *s, int *x, int *y, uint16_t *mask)
249 for(int screen = 0;
250 screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
251 screen++)
253 xcb_window_t root = xutil_screen_get(globalconf.connection, screen)->root;
255 if(mouse_query_pointer(root, x, y, mask))
257 *s = screen;
258 return true;
261 return false;
264 /** Grab the Pointer.
265 * \param window The window grabbed.
266 * \param cursor The cursor to display.
267 * \return True on success, false if an error occured.
269 static bool
270 mouse_grab_pointer(xcb_window_t window, xcb_cursor_t cursor)
272 xcb_grab_pointer_cookie_t grab_ptr_c;
273 xcb_grab_pointer_reply_t *grab_ptr_r;
275 grab_ptr_c = xcb_grab_pointer_unchecked(globalconf.connection, false, window,
276 MOUSEMASK, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
277 window, cursor, XCB_CURRENT_TIME);
278 grab_ptr_r = xcb_grab_pointer_reply(globalconf.connection, grab_ptr_c, NULL);
280 if(!grab_ptr_r)
281 return false;
283 p_delete(&grab_ptr_r);
285 return true;
288 /** Ungrab the Pointer
290 static inline void
291 mouse_ungrab_pointer(void)
293 xcb_ungrab_pointer(globalconf.connection, XCB_CURRENT_TIME);
296 /** Set the pointer position.
297 * \param window The destination window.
298 * \param x X-coordinate inside window.
299 * \param y Y-coordinate inside window.
301 static inline void
302 mouse_warp_pointer(xcb_window_t window, int x, int y)
304 xcb_warp_pointer(globalconf.connection, XCB_NONE, window,
305 0, 0, 0, 0, x, y );
308 /** Utility function to help with mouse-dragging
310 * \param x set to x-coordinate of the last event on return
311 * \param y set to y-coordinate of the last event on return
312 * \return true if an motion event was received
313 * false if an button release event was received
315 static bool
316 mouse_track_mouse_drag(int *x, int *y)
318 xcb_generic_event_t *ev;
319 xcb_motion_notify_event_t *ev_motion;
320 xcb_button_release_event_t *ev_button;
322 while(true)
323 while((ev = xcb_wait_for_event(globalconf.connection)))
324 switch((ev->response_type & 0x7F))
327 case XCB_MOTION_NOTIFY:
328 ev_motion = (xcb_motion_notify_event_t*) ev;
329 *x = ev_motion->event_x;
330 *y = ev_motion->event_y;
331 p_delete(&ev);
332 return true;
334 case XCB_BUTTON_RELEASE:
335 ev_button = (xcb_button_release_event_t*) ev;
336 *x = ev_button->event_x;
337 *y = ev_button->event_y;
338 p_delete(&ev);
339 return false;
341 default:
342 xcb_event_handle(&globalconf.evenths, ev);
343 p_delete(&ev);
344 break;
348 /** Resize a floating client with the mouse.
349 * \param c The client to resize.
350 * \param corner The corner to resize with.
351 * \param infobox Enable or disable the infobox.
353 static void
354 mouse_client_resize_floating(client_t *c, corner_t corner, bool infobox)
356 xcb_screen_t *screen;
357 /* one corner of the client has a fixed position */
358 int fixed_x, fixed_y;
359 /* the other is moved with the mouse */
360 int mouse_x = 0, mouse_y = 0;
361 /* the infobox */
362 simple_window_t sw;
363 xcb_cursor_t cursor;
364 int top, bottom, left, right;
366 /* do not resize fixed client */
367 if(client_isfixed(c))
368 return;
370 screen = xutil_screen_get(globalconf.connection, c->phys_screen);
372 /* get current mouse position */
373 mouse_query_pointer(screen->root, &mouse_x, &mouse_y, NULL);
375 top = c->geometry.y;
376 bottom = top + c->geometry.height;
377 left = c->geometry.x;
378 right = left + c->geometry.width;
380 /* figure out which corner to move */
381 corner = mouse_snap_to_corner(c->geometry, &mouse_x, &mouse_y, corner);
383 /* the opposite corner is fixed */
384 fixed_x = (mouse_x == left) ? right : left;
385 fixed_y = (mouse_y == top) ? bottom : top;
387 /* select cursor */
388 switch(corner)
390 default:
391 cursor = xcursor_new(globalconf.connection, XC_top_left_corner);
392 break;
393 case TopRightCorner:
394 cursor = xcursor_new(globalconf.connection, XC_top_right_corner);
395 break;
396 case BottomLeftCorner:
397 cursor = xcursor_new(globalconf.connection, XC_bottom_left_corner);
398 break;
399 case BottomRightCorner:
400 cursor = xcursor_new(globalconf.connection, XC_bottom_right_corner);
401 break;
404 /* grab the pointer */
405 if(!mouse_grab_pointer(screen->root, cursor))
406 return;
408 /* set pointer to the moveable corner */
409 mouse_warp_pointer(screen->root, mouse_x, mouse_y);
411 /* create the infobox */
412 if(infobox)
413 mouse_infobox_new(&sw, c->phys_screen, c->border, c->geometry);
415 /* for each motion event */
416 while(mouse_track_mouse_drag(&mouse_x, &mouse_y))
418 /* new client geometry */
419 area_t geo = { .x = MIN(fixed_x, mouse_x),
420 .y = MIN(fixed_y, mouse_y),
421 .width = (MAX(fixed_x, mouse_x) - MIN(fixed_x, mouse_x)),
422 .height = (MAX(fixed_y, mouse_y) - MIN(fixed_y, mouse_y)) };
423 /* new moveable corner */
424 corner_t new_corner;
426 if(mouse_x == AREA_LEFT(geo))
427 new_corner = (mouse_y == AREA_TOP(geo)) ? TopLeftCorner : BottomLeftCorner;
428 else
429 new_corner = (mouse_y == AREA_TOP(geo)) ? TopRightCorner : BottomRightCorner;
431 /* update cursor */
432 if(corner != new_corner)
434 corner = new_corner;
436 switch(corner)
438 default: cursor = xcursor_new(globalconf.connection, XC_top_left_corner); break;
439 case TopRightCorner: cursor = xcursor_new(globalconf.connection, XC_top_right_corner); break;
440 case BottomLeftCorner: cursor = xcursor_new(globalconf.connection, XC_bottom_left_corner); break;
441 case BottomRightCorner: cursor = xcursor_new(globalconf.connection, XC_bottom_right_corner); break;
444 xcb_change_active_pointer_grab(globalconf.connection, cursor,
445 XCB_CURRENT_TIME, MOUSEMASK);
448 if(c->hassizehints && c->honorsizehints)
450 int dx, dy;
452 /* apply size hints */
453 geo = client_geometry_hints(c, geo);
455 /* get the nonmoveable corner back onto fixed_x,fixed_y */
456 switch(corner)
458 default /* TopLeftCorner */:
459 dx = fixed_x - AREA_RIGHT(geo);
460 dy = fixed_y - AREA_BOTTOM(geo);
461 break;
462 case TopRightCorner:
463 dx = fixed_x - AREA_LEFT(geo);
464 dy = fixed_y - AREA_BOTTOM(geo);
465 break;
466 case BottomRightCorner:
467 dx = fixed_x - AREA_LEFT(geo);
468 dy = fixed_y - AREA_TOP(geo);
469 break;
470 case BottomLeftCorner:
471 dx = fixed_x - AREA_RIGHT(geo);
472 dy = fixed_y - AREA_TOP(geo);
473 break;
476 geo.x += dx;
477 geo.y += dy;
480 /* resize the client */
481 client_resize(c, geo, false);
483 /* refresh live */
484 wibox_refresh();
485 xcb_flush(globalconf.connection);
487 /* draw the infobox */
488 if(infobox)
489 mouse_infobox_draw(&sw, c->geometry, c->border);
492 /* relase pointer */
493 mouse_ungrab_pointer();
495 /* free the infobox */
496 if(infobox)
497 simplewindow_wipe(&sw);
500 /** Resize the master column/row of a tiled layout
501 * \param c A client on the tag/layout to resize.
503 static void
504 mouse_client_resize_tiled(client_t *c)
506 xcb_screen_t *screen;
507 /* screen area modulo wibox */
508 area_t area;
509 /* current tag */
510 tag_t *tag;
511 /* current layout */
512 layout_t *layout;
514 int mouse_x = 0, mouse_y = 0;
515 xcb_cursor_t cursor;
517 screen = xutil_screen_get(globalconf.connection, c->phys_screen);
518 tag = tags_get_current(c->screen)[0];
519 layout = tag->layout;
521 area = screen_area_get(tag->screen,
522 &globalconf.screens[tag->screen].wiboxes,
523 &globalconf.screens[tag->screen].padding,
524 true);
526 mouse_query_pointer(screen->root, &mouse_x, &mouse_y, NULL);
528 /* select initial pointer position */
529 if(layout == layout_tile)
531 mouse_x = area.x + area.width * tag->mwfact;
532 cursor = xcursor_new(globalconf.connection, XC_bottom_right_corner);
534 else if(layout == layout_tileleft)
536 mouse_x = area.x + area.width * (1. - tag->mwfact);
537 cursor = xcursor_new(globalconf.connection, XC_sb_h_double_arrow);
539 else if(layout == layout_tilebottom)
541 mouse_y = area.y + area.height * tag->mwfact;
542 cursor = xcursor_new(globalconf.connection, XC_sb_v_double_arrow);
544 else if(layout == layout_tiletop)
546 mouse_y = area.y + area.height * (1. - tag->mwfact);
547 cursor = xcursor_new(globalconf.connection, XC_sb_v_double_arrow);
549 else
550 return;
552 /* grab the pointer */
553 if(!mouse_grab_pointer(screen->root, cursor))
554 return;
556 /* set pointer to the moveable border */
557 mouse_warp_pointer(screen->root, mouse_x, mouse_y);
559 xcb_flush(globalconf.connection);
561 /* for each motion event */
562 while(mouse_track_mouse_drag(&mouse_x, &mouse_y))
564 double mwfact = 0, fact_x, fact_y;
566 /* calculate new master / rest ratio */
567 fact_x = (double) (mouse_x - area.x) / area.width;
568 fact_y = (double) (mouse_y - area.y) / area.height;
570 if(layout == layout_tile)
571 mwfact = fact_x;
572 else if(layout == layout_tileleft)
573 mwfact = 1. - fact_x;
574 else if(layout == layout_tilebottom)
575 mwfact = fact_y;
576 else if(layout == layout_tiletop)
577 mwfact = 1. - fact_y;
579 /* keep mwfact within reasonable bounds */
580 mwfact = MIN(MAX( 0.01, mwfact), 0.99);
582 /* refresh layout */
583 if(fabs(tag->mwfact - mwfact) >= 0.01)
585 tag->mwfact = mwfact;
586 globalconf.screens[tag->screen].need_arrange = true;
587 layout_refresh();
588 wibox_refresh();
589 xcb_flush(globalconf.connection);
593 /* relase pointer */
594 mouse_ungrab_pointer();
597 /** Resize the master client in mangifier layout
598 * \param c The client to resize.
599 * \param infobox Enable or disable the infobox.
601 static void
602 mouse_client_resize_magnified(client_t *c, bool infobox)
604 /* screen area modulo wibox */
605 area_t area;
606 /* center of area */
607 int center_x, center_y;
608 /* max. distance from the center */
609 double maxdist;
610 /* mouse position */
611 int mouse_x = 0, mouse_y = 0;
612 /* cursor while grabbing */
613 xcb_cursor_t cursor;
614 corner_t corner = AutoCorner;
615 /* current tag */
616 tag_t *tag;
617 /* the infobox */
618 simple_window_t sw;
619 xcb_window_t root;
621 tag = tags_get_current(c->screen)[0];
623 root = xutil_screen_get(globalconf.connection, c->phys_screen)->root;
625 area = screen_area_get(tag->screen,
626 &globalconf.screens[tag->screen].wiboxes,
627 &globalconf.screens[tag->screen].padding,
628 true);
630 center_x = area.x + (round(area.width / 2.));
631 center_y = area.y + (round(area.height / 2.));
633 maxdist = round(sqrt((area.width*area.width) + (area.height*area.height)) / 2.);
635 root = xutil_screen_get(globalconf.connection, c->phys_screen)->root;
637 if(!mouse_query_pointer(root, &mouse_x, &mouse_y, NULL))
638 return;
640 /* select corner */
641 corner = mouse_snap_to_corner(c->geometry, &mouse_x, &mouse_y, corner);
643 /* select cursor */
644 switch(corner)
646 default:
647 cursor = xcursor_new(globalconf.connection, XC_top_left_corner);
648 break;
649 case TopRightCorner:
650 cursor = xcursor_new(globalconf.connection, XC_top_right_corner);
651 break;
652 case BottomLeftCorner:
653 cursor = xcursor_new(globalconf.connection, XC_bottom_left_corner);
654 break;
655 case BottomRightCorner:
656 cursor = xcursor_new(globalconf.connection, XC_bottom_right_corner);
657 break;
660 /* grab pointer */
661 if(!mouse_grab_pointer(root, cursor))
662 return;
664 /* move pointer to corner */
665 mouse_warp_pointer(root, mouse_x, mouse_y);
667 /* create the infobox */
668 if(infobox)
669 mouse_infobox_new(&sw, c->phys_screen, c->border, c->geometry);
671 /* for each motion event */
672 while(mouse_track_mouse_drag(&mouse_x, &mouse_y))
674 /* \todo keep pointer on screen diagonals */
675 double mwfact, dist, dx, dy;
677 /* calc distance from the center */
678 dx = center_x - mouse_x;
679 dy = center_y - mouse_y;
680 dist = sqrt((dx * dx) + (dy * dy));
682 /* new master/rest ratio */
683 mwfact = (dist * dist) / (maxdist * maxdist);
685 /* keep mwfact within reasonable bounds */
686 mwfact = MIN(MAX( 0.01, mwfact), 0.99);
688 /* refresh the layout */
689 if(fabs(tag->mwfact - mwfact) >= 0.01)
691 tag->mwfact = mwfact;
692 globalconf.screens[tag->screen].need_arrange = true;
693 layout_refresh();
694 wibox_refresh();
695 xcb_flush(globalconf.connection);
698 /* draw the infobox */
699 if(infobox)
700 mouse_infobox_draw(&sw, c->geometry, c->border);
703 /* ungrab pointer */
704 mouse_ungrab_pointer();
706 /* free the infobox */
707 if(infobox)
708 simplewindow_wipe(&sw);
711 /** Resize a client with the mouse.
712 * \param c The client to resize.
713 * \param corner The corner to use.
714 * \param infobox Enable or disable the info box.
716 static void
717 mouse_client_resize(client_t *c, corner_t corner, bool infobox)
719 int n, screen;
720 tag_t **curtags;
721 layout_t *layout;
722 xcb_screen_t *s;
724 if(c->isfullscreen
725 || c->type == WINDOW_TYPE_DESKTOP
726 || c->type == WINDOW_TYPE_SPLASH
727 || c->type == WINDOW_TYPE_DOCK)
728 return;
730 curtags = tags_get_current(c->screen);
731 layout = curtags[0]->layout;
732 s = xutil_screen_get(globalconf.connection, c->phys_screen);
734 /* only handle floating, tiled and magnifier layouts */
735 if(layout == layout_floating || client_isfloating(c))
736 mouse_client_resize_floating(c, corner, infobox);
737 else if(layout == layout_tile || layout == layout_tileleft
738 || layout == layout_tilebottom || layout == layout_tiletop)
740 screen = c->screen;
741 for(n = 0, c = globalconf.clients; c; c = c->next)
742 if(IS_TILED(c, screen))
743 n++;
745 /* only masters on this screen? */
746 if(n <= curtags[0]->nmaster)
747 goto bailout;
749 /* no tiled clients on this screen? */
750 for(c = globalconf.clients; c && !IS_TILED(c, screen); c = c->next);
751 if(!c)
752 goto bailout;
754 mouse_client_resize_tiled(c);
756 else if(layout == layout_magnifier)
757 mouse_client_resize_magnified(c, infobox);
759 bailout:
760 p_delete(&curtags);
763 /** Resize a client with mouse.
764 * \param L The Lua VM state.
766 * \luastack
767 * \lvalue A client.
768 * \lparam An optional table with keys: `corner', such as bottomleft,
769 * topright, etc, to specify which corner to grab (default to auto) and
770 * `infobox' to enable or disable the coordinates and dimensions box (default to
771 * enabled).
774 luaA_client_mouse_resize(lua_State *L)
776 client_t **c = luaA_checkudata(L, 1, "client");
777 corner_t corner = AutoCorner;
778 bool infobox = true;
779 size_t len;
780 const char *buf;
782 if(lua_gettop(L) == 2 && !lua_isnil(L, 2))
784 luaA_checktable(L, 2);
785 buf = luaA_getopt_lstring(L, 2, "corner", "auto", &len);
786 corner = a_strtocorner(buf, len);
787 infobox = luaA_getopt_boolean(L, 2, "infobox", true);
790 mouse_client_resize(*c, corner, infobox);
792 return 0;
795 /** Create a new mouse button bindings.
796 * \param L The Lua VM state.
797 * \return The number of elements pushed on stack.
798 * \luastack
799 * \lparam A table with modifiers keys, or a button to clone.
800 * \lparam A mouse button number.
801 * \lparam A function to execute on click events.
802 * \lparam A function to execute on release events.
803 * \lreturn A mouse button binding.
805 static int
806 luaA_button_new(lua_State *L)
808 int i, len;
809 button_t *button, **orig;
811 if((orig = luaA_toudata(L, 2, "button")))
813 button_t *copy = p_new(button_t, 1);
814 copy->mod = (*orig)->mod;
815 copy->button = (*orig)->button;
816 if((*orig)->press != LUA_REFNIL)
818 lua_rawgeti(L, LUA_REGISTRYINDEX, (*orig)->press);
819 luaA_registerfct(L, -1, &copy->press);
821 else
822 copy->press = LUA_REFNIL;
823 if((*orig)->release != LUA_REFNIL)
825 lua_rawgeti(L, LUA_REGISTRYINDEX, (*orig)->release);
826 luaA_registerfct(L, -1, &copy->release);
828 else
829 copy->release = LUA_REFNIL;
830 return luaA_button_userdata_new(L, copy);
833 luaA_checktable(L, 2);
834 /* arg 3 is mouse button */
835 i = luaL_checknumber(L, 3);
836 /* arg 4 and 5 are callback functions */
837 if(!lua_isnil(L, 4))
838 luaA_checkfunction(L, 4);
839 if(lua_gettop(L) == 5 && !lua_isnil(L, 5))
840 luaA_checkfunction(L, 5);
842 button = p_new(button_t, 1);
843 button->button = xutil_button_fromint(i);
845 if(lua_isnil(L, 4))
846 button->press = LUA_REFNIL;
847 else
848 luaA_registerfct(L, 4, &button->press);
850 if(lua_gettop(L) == 5 && !lua_isnil(L, 5))
851 luaA_registerfct(L, 5, &button->release);
852 else
853 button->release = LUA_REFNIL;
855 len = lua_objlen(L, 2);
856 for(i = 1; i <= len; i++)
858 size_t blen;
859 const char *buf;
860 lua_rawgeti(L, 2, i);
861 buf = luaL_checklstring(L, -1, &blen);
862 button->mod |= xutil_key_mask_fromstr(buf, blen);
865 return luaA_button_userdata_new(L, button);
868 /** Set a button array with a Lua table.
869 * \param L The Lua VM state.
870 * \param idx The index of the Lua table.
871 * \param buttons The array button to fill.
873 void
874 luaA_button_array_set(lua_State *L, int idx, button_array_t *buttons)
876 button_t **b;
878 luaA_checktable(L, idx);
879 button_array_wipe(buttons);
880 button_array_init(buttons);
881 lua_pushnil(L);
882 while(lua_next(L, idx))
884 b = luaA_checkudata(L, -1, "button");
885 button_array_append(buttons, *b);
886 button_ref(b);
887 lua_pop(L, 1);
891 /** Push an array of button as an Lua table onto the stack.
892 * \param L The Lua VM state.
893 * \param buttons The button array to push.
894 * \return The number of elements pushed on stack.
897 luaA_button_array_get(lua_State *L, button_array_t *buttons)
899 luaA_otable_new(L);
900 for(int i = 0; i < buttons->len; i++)
902 luaA_button_userdata_new(L, buttons->tab[i]);
903 lua_rawseti(L, -2, i + 1);
905 return 1;
908 /** Button object.
909 * \param L The Lua VM state.
910 * \return The number of elements pushed on stack.
911 * \luastack
912 * \lfield press The function called when button press event is received.
913 * \lfield release The function called when button release event is received.
915 static int
916 luaA_button_index(lua_State *L)
918 if(luaA_usemetatable(L, 1, 2))
919 return 1;
921 size_t len;
922 button_t **button = luaA_checkudata(L, 1, "button");
923 const char *attr = luaL_checklstring(L, 2, &len);
925 switch(a_tokenize(attr, len))
927 case A_TK_PRESS:
928 if((*button)->press != LUA_REFNIL)
929 lua_rawgeti(L, LUA_REGISTRYINDEX, (*button)->press);
930 else
931 lua_pushnil(L);
932 break;
933 case A_TK_RELEASE:
934 if((*button)->release != LUA_REFNIL)
935 lua_rawgeti(L, LUA_REGISTRYINDEX, (*button)->release);
936 else
937 lua_pushnil(L);
938 break;
939 case A_TK_BUTTON:
940 /* works fine, but not *really* neat */
941 lua_pushnumber(L, (*button)->button);
942 break;
943 default:
944 break;
947 return 1;
950 /** Button object.
951 * \param L The Lua VM state.
952 * \return The number of elements pushed on stack.
953 * \luastack
955 static int
956 luaA_button_newindex(lua_State *L)
958 if(luaA_usemetatable(L, 1, 2))
959 return 1;
961 size_t len;
962 button_t **button = luaA_checkudata(L, 1, "button");
963 const char *attr = luaL_checklstring(L, 2, &len);
965 switch(a_tokenize(attr, len))
967 case A_TK_PRESS:
968 luaA_registerfct(L, 3, &(*button)->press);
969 break;
970 case A_TK_RELEASE:
971 luaA_registerfct(L, 3, &(*button)->release);
972 break;
973 case A_TK_BUTTON:
974 (*button)->button = xutil_button_fromint(luaL_checknumber(L, 3));
975 break;
976 default:
977 break;
980 return 0;
983 const struct luaL_reg awesome_button_methods[] =
985 { "__call", luaA_button_new },
986 { NULL, NULL }
988 const struct luaL_reg awesome_button_meta[] =
990 { "__index", luaA_button_index },
991 { "__newindex", luaA_button_newindex },
992 { "__gc", luaA_button_gc },
993 { "__eq", luaA_button_eq },
994 { "__tostring", luaA_button_tostring },
995 { NULL, NULL }
998 /** Mouse library.
999 * \param L The Lua VM state.
1000 * \return The number of elements pushed on stack.
1001 * \luastack
1002 * \lfield coords Mouse coordinates.
1003 * \lfield screen Mouse screen number.
1005 static int
1006 luaA_mouse_index(lua_State *L)
1008 size_t len;
1009 const char *attr = luaL_checklstring(L, 2, &len);
1010 int mouse_x, mouse_y, i;
1011 int screen;
1013 switch(a_tokenize(attr, len))
1015 case A_TK_SCREEN:
1016 if(!mouse_query_pointer_root(&screen, &mouse_x, &mouse_y, NULL))
1017 return 0;
1019 i = screen_getbycoord(screen, mouse_x, mouse_y);
1021 lua_pushnumber(L, i + 1);
1022 break;
1023 default:
1024 return 0;
1027 return 1;
1030 /** Newindex for mouse.
1031 * \param L The Lua VM state.
1032 * \return The number of elements pushed on stack.
1034 static int
1035 luaA_mouse_newindex(lua_State *L)
1037 size_t len;
1038 const char *attr = luaL_checklstring(L, 2, &len);
1039 int x, y = 0;
1040 xcb_window_t root;
1041 int screen, phys_screen;
1043 switch(a_tokenize(attr, len))
1045 case A_TK_SCREEN:
1046 screen = luaL_checknumber(L, 3) - 1;
1047 luaA_checkscreen(screen);
1049 /* we need the physical one to get the root window */
1050 phys_screen = screen_virttophys(screen);
1051 root = xutil_screen_get(globalconf.connection, phys_screen)->root;
1053 x = globalconf.screens[screen].geometry.x;
1054 y = globalconf.screens[screen].geometry.y;
1056 mouse_warp_pointer(root, x, y);
1057 break;
1058 default:
1059 return 0;
1062 return 0;
1065 /** Push a table with mouse status.
1066 * \param L The Lua VM state.
1067 * \param x The x coordinate.
1068 * \param y The y coordinate.
1069 * \param mask The button mask.
1072 luaA_mouse_pushstatus(lua_State *L, int x, int y, uint16_t mask)
1074 lua_newtable(L);
1075 lua_pushnumber(L, x);
1076 lua_setfield(L, -2, "x");
1077 lua_pushnumber(L, y);
1078 lua_setfield(L, -2, "y");
1080 lua_newtable(L);
1082 int i = 1;
1084 for(uint16_t maski = XCB_BUTTON_MASK_1; maski <= XCB_BUTTON_MASK_5; maski <<= 1)
1086 if(mask & maski)
1087 lua_pushboolean(L, true);
1088 else
1089 lua_pushboolean(L, false);
1090 lua_rawseti(L, -2, i++);
1092 lua_setfield(L, -2, "buttons");
1093 return 1;
1096 /** Get or set the mouse coords.
1097 * \param L The Lua VM state.
1098 * \return The number of elements pushed on stack.
1099 * \luastack
1100 * \lparam None or a table with x and y keys as mouse coordinates.
1101 * \lreturn A table with mouse coordinates.
1103 static int
1104 luaA_mouse_coords(lua_State *L)
1106 uint16_t mask;
1107 int screen, x, y, mouse_x, mouse_y;
1109 if(lua_gettop(L) == 1)
1111 xcb_window_t root;
1113 luaA_checktable(L, 1);
1115 if(!mouse_query_pointer_root(&screen, &mouse_x, &mouse_y, &mask))
1116 return 0;
1118 x = luaA_getopt_number(L, 1, "x", mouse_x);
1119 y = luaA_getopt_number(L, 1, "y", mouse_y);
1121 root = xutil_screen_get(globalconf.connection, screen)->root;
1122 mouse_warp_pointer(root, x, y);
1123 lua_pop(L, 1);
1126 if(!mouse_query_pointer_root(&screen, &mouse_x, &mouse_y, &mask))
1127 return 0;
1129 return luaA_mouse_pushstatus(L, mouse_x, mouse_y, mask);
1132 /** Get the client which is under the pointer.
1133 * \param L The Lua VM state.
1134 * \return The number of elements pushed on stack.
1135 * \luastack
1136 * \lreturn A client or nil.
1138 static int
1139 luaA_mouse_client_under_pointer(lua_State *L)
1141 for(int screen = 0;
1142 screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
1143 screen++)
1145 xcb_window_t root = xutil_screen_get(globalconf.connection, screen)->root;
1146 xcb_query_pointer_reply_t *query_ptr_r;
1147 xcb_query_pointer_cookie_t query_ptr_c = xcb_query_pointer_unchecked(globalconf.connection, root);
1148 query_ptr_r = xcb_query_pointer_reply(globalconf.connection, query_ptr_c, NULL);
1150 if(query_ptr_r)
1152 client_t *c = client_getbywin(query_ptr_r->child);
1153 p_delete(&query_ptr_r);
1154 if(c)
1155 return luaA_client_userdata_new(L, c);
1156 else
1158 lua_pushnil(L);
1159 return 1;
1163 return 0;
1166 const struct luaL_reg awesome_mouse_methods[] =
1168 { "__index", luaA_mouse_index },
1169 { "__newindex", luaA_mouse_newindex },
1170 { "coords", luaA_mouse_coords },
1171 { "client_under_pointer", luaA_mouse_client_under_pointer },
1172 { NULL, NULL }
1174 const struct luaL_reg awesome_mouse_meta[] =
1176 { NULL, NULL }
1179 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80