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.
24 #include "common/tokenize.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. */
53 /** Convert a corner name into a corner type.
54 * \param str A string.
55 * \param len String length.
56 * \return A corner type.
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
;
72 * \param button The button to destroy.
75 button_delete(button_t
**button
)
77 luaL_unref(globalconf
.L
, LUA_REGISTRYINDEX
, (*button
)->press
);
78 luaL_unref(globalconf
.L
, LUA_REGISTRYINDEX
, (*button
)->release
);
82 /** Set coordinates to a corner of an 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.
94 mouse_snap_to_corner(area_t a
, int *x
, int *y
, corner_t corner
)
96 int top
, bottom
, left
, right
;
99 bottom
= AREA_BOTTOM(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
;
111 corner
= TopRightCorner
;
115 if(abs(left
- *x
) < abs(right
- *x
))
116 corner
= BottomLeftCorner
;
118 corner
= BottomRightCorner
;
134 case BottomLeftCorner
:
139 case BottomRightCorner
:
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.
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
};
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.
186 mouse_infobox_new(simple_window_t
*sw
, int phys_screen
, int border
, area_t geometry
)
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,
197 geom
.x
= geometry
.x
+ ((2 * border
+ geometry
.width
) - geom
.width
) / 2;
198 geom
.y
= geometry
.y
+ ((2 * border
+ geometry
.height
) - geom
.height
) / 2;
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
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
)
229 *x
= query_ptr_r
->win_x
;
230 *y
= query_ptr_r
->win_y
;
232 *mask
= query_ptr_r
->mask
;
234 p_delete(&query_ptr_r
);
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.
247 mouse_query_pointer_root(int *s
, int *x
, int *y
, uint16_t *mask
)
250 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
253 xcb_window_t root
= xutil_screen_get(globalconf
.connection
, screen
)->root
;
255 if(mouse_query_pointer(root
, x
, y
, mask
))
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.
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
);
283 p_delete(&grab_ptr_r
);
288 /** Ungrab the Pointer
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.
302 mouse_warp_pointer(xcb_window_t window
, int x
, int y
)
304 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, window
,
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
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
;
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
;
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
;
342 xcb_event_handle(&globalconf
.evenths
, ev
);
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.
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;
364 int top
, bottom
, left
, right
;
366 /* do not resize fixed client */
367 if(client_isfixed(c
))
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
);
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
;
391 cursor
= xcursor_new(globalconf
.connection
, XC_top_left_corner
);
394 cursor
= xcursor_new(globalconf
.connection
, XC_top_right_corner
);
396 case BottomLeftCorner
:
397 cursor
= xcursor_new(globalconf
.connection
, XC_bottom_left_corner
);
399 case BottomRightCorner
:
400 cursor
= xcursor_new(globalconf
.connection
, XC_bottom_right_corner
);
404 /* grab the pointer */
405 if(!mouse_grab_pointer(screen
->root
, cursor
))
408 /* set pointer to the moveable corner */
409 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
411 /* create the 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 */
426 if(mouse_x
== AREA_LEFT(geo
))
427 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopLeftCorner
: BottomLeftCorner
;
429 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopRightCorner
: BottomRightCorner
;
432 if(corner
!= new_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
)
452 /* apply size hints */
453 geo
= client_geometry_hints(c
, geo
);
455 /* get the nonmoveable corner back onto fixed_x,fixed_y */
458 default /* TopLeftCorner */:
459 dx
= fixed_x
- AREA_RIGHT(geo
);
460 dy
= fixed_y
- AREA_BOTTOM(geo
);
463 dx
= fixed_x
- AREA_LEFT(geo
);
464 dy
= fixed_y
- AREA_BOTTOM(geo
);
466 case BottomRightCorner
:
467 dx
= fixed_x
- AREA_LEFT(geo
);
468 dy
= fixed_y
- AREA_TOP(geo
);
470 case BottomLeftCorner
:
471 dx
= fixed_x
- AREA_RIGHT(geo
);
472 dy
= fixed_y
- AREA_TOP(geo
);
480 /* resize the client */
481 client_resize(c
, geo
, false);
485 xcb_flush(globalconf
.connection
);
487 /* draw the infobox */
489 mouse_infobox_draw(&sw
, c
->geometry
, c
->border
);
493 mouse_ungrab_pointer();
495 /* free the 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.
504 mouse_client_resize_tiled(client_t
*c
)
506 xcb_screen_t
*screen
;
507 /* screen area modulo wibox */
514 int mouse_x
= 0, mouse_y
= 0;
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
,
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
);
552 /* grab the pointer */
553 if(!mouse_grab_pointer(screen
->root
, cursor
))
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
)
572 else if(layout
== layout_tileleft
)
573 mwfact
= 1. - fact_x
;
574 else if(layout
== layout_tilebottom
)
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);
583 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
585 tag
->mwfact
= mwfact
;
586 globalconf
.screens
[tag
->screen
].need_arrange
= true;
589 xcb_flush(globalconf
.connection
);
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.
602 mouse_client_resize_magnified(client_t
*c
, bool infobox
)
604 /* screen area modulo wibox */
607 int center_x
, center_y
;
608 /* max. distance from the center */
611 int mouse_x
= 0, mouse_y
= 0;
612 /* cursor while grabbing */
614 corner_t corner
= AutoCorner
;
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
,
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
))
641 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
647 cursor
= xcursor_new(globalconf
.connection
, XC_top_left_corner
);
650 cursor
= xcursor_new(globalconf
.connection
, XC_top_right_corner
);
652 case BottomLeftCorner
:
653 cursor
= xcursor_new(globalconf
.connection
, XC_bottom_left_corner
);
655 case BottomRightCorner
:
656 cursor
= xcursor_new(globalconf
.connection
, XC_bottom_right_corner
);
661 if(!mouse_grab_pointer(root
, cursor
))
664 /* move pointer to corner */
665 mouse_warp_pointer(root
, mouse_x
, mouse_y
);
667 /* create the 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;
695 xcb_flush(globalconf
.connection
);
698 /* draw the infobox */
700 mouse_infobox_draw(&sw
, c
->geometry
, c
->border
);
704 mouse_ungrab_pointer();
706 /* free the 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.
717 mouse_client_resize(client_t
*c
, corner_t corner
, bool infobox
)
725 || c
->type
== WINDOW_TYPE_DESKTOP
726 || c
->type
== WINDOW_TYPE_SPLASH
727 || c
->type
== WINDOW_TYPE_DOCK
)
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
)
741 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
)
742 if(IS_TILED(c
, screen
))
745 /* only masters on this screen? */
746 if(n
<= curtags
[0]->nmaster
)
749 /* no tiled clients on this screen? */
750 for(c
= globalconf
.clients
; c
&& !IS_TILED(c
, screen
); c
= c
->next
);
754 mouse_client_resize_tiled(c
);
756 else if(layout
== layout_magnifier
)
757 mouse_client_resize_magnified(c
, infobox
);
763 /** Resize a client with mouse.
764 * \param L The Lua VM state.
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
774 luaA_client_mouse_resize(lua_State
*L
)
776 client_t
**c
= luaA_checkudata(L
, 1, "client");
777 corner_t corner
= AutoCorner
;
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
);
795 /** Create a new mouse button bindings.
796 * \param L The Lua VM state.
797 * \return The number of elements pushed on stack.
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.
806 luaA_button_new(lua_State
*L
)
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, ©
->press
);
822 copy
->press
= LUA_REFNIL
;
823 if((*orig
)->release
!= LUA_REFNIL
)
825 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*orig
)->release
);
826 luaA_registerfct(L
, -1, ©
->release
);
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 */
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
);
846 button
->press
= LUA_REFNIL
;
848 luaA_registerfct(L
, 4, &button
->press
);
850 if(lua_gettop(L
) == 5 && !lua_isnil(L
, 5))
851 luaA_registerfct(L
, 5, &button
->release
);
853 button
->release
= LUA_REFNIL
;
855 len
= lua_objlen(L
, 2);
856 for(i
= 1; i
<= len
; i
++)
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.
874 luaA_button_array_set(lua_State
*L
, int idx
, button_array_t
*buttons
)
878 luaA_checktable(L
, idx
);
879 button_array_wipe(buttons
);
880 button_array_init(buttons
);
882 while(lua_next(L
, idx
))
884 b
= luaA_checkudata(L
, -1, "button");
885 button_array_append(buttons
, *b
);
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
)
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);
909 * \param L The Lua VM state.
910 * \return The number of elements pushed on stack.
912 * \lfield press The function called when button press event is received.
913 * \lfield release The function called when button release event is received.
916 luaA_button_index(lua_State
*L
)
918 if(luaA_usemetatable(L
, 1, 2))
922 button_t
**button
= luaA_checkudata(L
, 1, "button");
923 const char *attr
= luaL_checklstring(L
, 2, &len
);
925 switch(a_tokenize(attr
, len
))
928 if((*button
)->press
!= LUA_REFNIL
)
929 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*button
)->press
);
934 if((*button
)->release
!= LUA_REFNIL
)
935 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*button
)->release
);
940 /* works fine, but not *really* neat */
941 lua_pushnumber(L
, (*button
)->button
);
951 * \param L The Lua VM state.
952 * \return The number of elements pushed on stack.
956 luaA_button_newindex(lua_State
*L
)
958 if(luaA_usemetatable(L
, 1, 2))
962 button_t
**button
= luaA_checkudata(L
, 1, "button");
963 const char *attr
= luaL_checklstring(L
, 2, &len
);
965 switch(a_tokenize(attr
, len
))
968 luaA_registerfct(L
, 3, &(*button
)->press
);
971 luaA_registerfct(L
, 3, &(*button
)->release
);
974 (*button
)->button
= xutil_button_fromint(luaL_checknumber(L
, 3));
983 const struct luaL_reg awesome_button_methods
[] =
985 { "__call", luaA_button_new
},
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
},
999 * \param L The Lua VM state.
1000 * \return The number of elements pushed on stack.
1002 * \lfield coords Mouse coordinates.
1003 * \lfield screen Mouse screen number.
1006 luaA_mouse_index(lua_State
*L
)
1009 const char *attr
= luaL_checklstring(L
, 2, &len
);
1010 int mouse_x
, mouse_y
, i
;
1013 switch(a_tokenize(attr
, len
))
1016 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, NULL
))
1019 i
= screen_getbycoord(screen
, mouse_x
, mouse_y
);
1021 lua_pushnumber(L
, i
+ 1);
1030 /** Newindex for mouse.
1031 * \param L The Lua VM state.
1032 * \return The number of elements pushed on stack.
1035 luaA_mouse_newindex(lua_State
*L
)
1038 const char *attr
= luaL_checklstring(L
, 2, &len
);
1041 int screen
, phys_screen
;
1043 switch(a_tokenize(attr
, len
))
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
);
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
)
1075 lua_pushnumber(L
, x
);
1076 lua_setfield(L
, -2, "x");
1077 lua_pushnumber(L
, y
);
1078 lua_setfield(L
, -2, "y");
1084 for(uint16_t maski
= XCB_BUTTON_MASK_1
; maski
<= XCB_BUTTON_MASK_5
; maski
<<= 1)
1087 lua_pushboolean(L
, true);
1089 lua_pushboolean(L
, false);
1090 lua_rawseti(L
, -2, i
++);
1092 lua_setfield(L
, -2, "buttons");
1096 /** Get or set the mouse coords.
1097 * \param L The Lua VM state.
1098 * \return The number of elements pushed on stack.
1100 * \lparam None or a table with x and y keys as mouse coordinates.
1101 * \lreturn A table with mouse coordinates.
1104 luaA_mouse_coords(lua_State
*L
)
1107 int screen
, x
, y
, mouse_x
, mouse_y
;
1109 if(lua_gettop(L
) == 1)
1113 luaA_checktable(L
, 1);
1115 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, &mask
))
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
);
1126 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, &mask
))
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.
1136 * \lreturn A client or nil.
1139 luaA_mouse_client_under_pointer(lua_State
*L
)
1142 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
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
);
1152 client_t
*c
= client_getbywin(query_ptr_r
->child
);
1153 p_delete(&query_ptr_r
);
1155 return luaA_client_userdata_new(L
, c
);
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
},
1174 const struct luaL_reg awesome_mouse_meta
[] =
1179 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80