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"
32 #define MOUSEMASK (XCB_EVENT_MASK_BUTTON_PRESS \
33 | XCB_EVENT_MASK_BUTTON_RELEASE \
34 | XCB_EVENT_MASK_POINTER_MOTION)
36 extern awesome_t globalconf
;
38 DO_LUA_NEW(extern, button_t
, button
, "button", button_ref
)
39 DO_LUA_GC(button_t
, button
, "button", button_unref
)
40 DO_LUA_EQ(button_t
, button
, "button")
42 /** Define corners. */
52 /** Convert a corner name into a corner type.
53 * \param str A string.
54 * \param len String length.
55 * \return A corner type.
58 a_strtocorner(const char *str
, size_t len
)
60 switch (a_tokenize(str
, len
))
62 case A_TK_BOTTOMRIGHT
: return BottomRightCorner
;
63 case A_TK_BOTTOMLEFT
: return BottomLeftCorner
;
64 case A_TK_TOPLEFT
: return TopLeftCorner
;
65 case A_TK_TOPRIGHT
: return TopRightCorner
;
66 default: return AutoCorner
;
71 * \param button The button to destroy.
74 button_delete(button_t
**button
)
76 luaL_unref(globalconf
.L
, LUA_REGISTRYINDEX
, (*button
)->press
);
77 luaL_unref(globalconf
.L
, LUA_REGISTRYINDEX
, (*button
)->release
);
81 /** Snap an area to the outside of an area.
82 * \param geometry geometry of the area to snap
83 * \param snap_geometry geometry of snapping area
84 * \param snap snap trigger in pixel
85 * \return snapped geometry
88 mouse_snapclienttogeometry_outside(area_t geometry
, area_t snap_geometry
, int snap
)
90 if(geometry
.x
< snap
+ snap_geometry
.x
+ snap_geometry
.width
91 && geometry
.x
> snap_geometry
.x
+ snap_geometry
.width
)
92 geometry
.x
= snap_geometry
.x
+ snap_geometry
.width
;
93 else if(geometry
.x
+ geometry
.width
< snap_geometry
.x
94 && geometry
.x
+ geometry
.width
> snap_geometry
.x
- snap
)
95 geometry
.x
= snap_geometry
.x
- geometry
.width
;
97 if(geometry
.y
< snap
+ snap_geometry
.y
+ snap_geometry
.height
98 && geometry
.y
> snap_geometry
.y
+ snap_geometry
.height
)
99 geometry
.y
= snap_geometry
.y
+ snap_geometry
.height
;
100 else if(geometry
.y
+ geometry
.height
< snap_geometry
.y
101 && geometry
.y
+ geometry
.height
> snap_geometry
.y
- snap
)
102 geometry
.y
= snap_geometry
.y
- geometry
.height
;
107 /** Snap an area to the inside of an area.
108 * \param geometry geometry of the area to snap
109 * \param snap_geometry geometry of snapping area
110 * \param snap snap trigger in pixel
111 * \return snapped geometry
114 mouse_snapclienttogeometry_inside(area_t geometry
, area_t snap_geometry
, int snap
)
116 if(abs(geometry
.x
) < snap
+ snap_geometry
.x
&& geometry
.x
> snap_geometry
.x
)
117 geometry
.x
= snap_geometry
.x
;
118 else if(abs((snap_geometry
.x
+ snap_geometry
.width
) - (geometry
.x
+ geometry
.width
))
120 geometry
.x
= snap_geometry
.x
+ snap_geometry
.width
- geometry
.width
;
121 if(abs(geometry
.y
) < snap
+ snap_geometry
.y
&& geometry
.y
> snap_geometry
.y
)
122 geometry
.y
= snap_geometry
.y
;
123 else if(abs((snap_geometry
.y
+ snap_geometry
.height
) - (geometry
.y
+ geometry
.height
))
125 geometry
.y
= snap_geometry
.y
+ snap_geometry
.height
- geometry
.height
;
130 /** Snap a client with a future geometry to the screen and other clients.
131 * \param c The client.
132 * \param geometry Geometry the client will get.
133 * \param snap The maximum distance in pixels to trigger a "snap".
134 * \return Geometry to set to the client.
137 mouse_snapclient(client_t
*c
, area_t geometry
, int snap
)
140 area_t snapper_geometry
;
141 area_t screen_geometry
=
142 screen_area_get(c
->screen
,
143 &globalconf
.screens
[c
->screen
].wiboxes
,
144 &globalconf
.screens
[c
->screen
].padding
,
147 area_t screen_geometry_barless
=
148 screen_area_get(c
->screen
,
150 &globalconf
.screens
[c
->screen
].padding
,
153 geometry
= titlebar_geometry_add(c
->titlebar
, c
->border
, geometry
);
156 mouse_snapclienttogeometry_inside(geometry
, screen_geometry
, snap
);
159 mouse_snapclienttogeometry_inside(geometry
, screen_geometry_barless
, snap
);
161 for(snapper
= globalconf
.clients
; snapper
; snapper
= snapper
->next
)
162 if(snapper
!= c
&& client_isvisible(snapper
, c
->screen
))
164 snapper_geometry
= snapper
->geometry
;
165 snapper_geometry
= titlebar_geometry_add(snapper
->titlebar
, snapper
->border
, snapper_geometry
);
167 mouse_snapclienttogeometry_outside(geometry
,
172 return titlebar_geometry_remove(c
->titlebar
, c
->border
, geometry
);
175 /** Set coordinates to a corner of an area.
178 * \param[in,out] x The x coordinate.
179 * \param[in,out] y The y coordinate.
180 * \param corner The corner to snap to.
181 * \return The corner the coordinates have been set to. If corner != AutoCorner
182 * this is always equal to \em corner.
184 * \todo rename/move this is still awkward and might be useful somewhere else.
187 mouse_snap_to_corner(area_t a
, int *x
, int *y
, corner_t corner
)
189 int top
, bottom
, left
, right
;
192 bottom
= AREA_BOTTOM(a
);
194 right
= AREA_RIGHT(a
);
196 /* figure out the nearser corner */
197 if(corner
== AutoCorner
)
199 if(abs(top
- *y
) < abs(bottom
- *y
))
201 if(abs(left
- *x
) < abs(right
- *x
))
202 corner
= TopLeftCorner
;
204 corner
= TopRightCorner
;
208 if(abs(left
- *x
) < abs(right
- *x
))
209 corner
= BottomLeftCorner
;
211 corner
= BottomRightCorner
;
227 case BottomLeftCorner
:
232 case BottomRightCorner
:
244 /** Redraw the infobox.
245 * \param sw The simple window.
246 * \param geometry The geometry to use for the box.
247 * \param border The client border size.
250 mouse_infobox_draw(simple_window_t
*sw
, area_t geometry
, int border
)
252 area_t draw_geometry
= { 0, 0, sw
->ctx
.width
, sw
->ctx
.height
};
255 xcolor_t color_without_alpha
= globalconf
.colors
.bg
;
256 color_without_alpha
.alpha
= 0xffff;
258 len
= snprintf(size
, sizeof(size
), "<text align=\"center\"/>%dx%d+%d+%d",
259 geometry
.width
, geometry
.height
, geometry
.x
, geometry
.y
);
260 draw_rectangle(&sw
->ctx
, draw_geometry
, 1.0, true, &color_without_alpha
);
261 draw_text(&sw
->ctx
, globalconf
.font
, PANGO_ELLIPSIZE_NONE
, PANGO_WRAP_WORD
, draw_geometry
, size
, len
, NULL
);
262 simplewindow_move(sw
,
263 geometry
.x
+ ((2 * border
+ geometry
.width
) - sw
->geometry
.width
) / 2,
264 geometry
.y
+ ((2 * border
+ geometry
.height
) - sw
->geometry
.height
) / 2);
265 simplewindow_refresh_pixmap(sw
);
266 xcb_flush(globalconf
.connection
);
269 #define MOUSE_INFOBOX_STRING_DEFAULT "0000x0000+0000+0000"
271 /** Initialize the infobox window.
272 * \param sw The simple window to init.
273 * \param phys_screen Physical screen number.
274 * \param border Border size of the client.
275 * \param geometry Client geometry.
276 * \return The simple window.
279 mouse_infobox_new(simple_window_t
*sw
, int phys_screen
, int border
, area_t geometry
)
282 draw_parser_data_t pdata
;
284 draw_parser_data_init(&pdata
);
286 geom
= draw_text_extents(globalconf
.font
,
287 MOUSE_INFOBOX_STRING_DEFAULT
,
288 sizeof(MOUSE_INFOBOX_STRING_DEFAULT
)-1,
290 geom
.x
= geometry
.x
+ ((2 * border
+ geometry
.width
) - geom
.width
) / 2;
291 geom
.y
= geometry
.y
+ ((2 * border
+ geometry
.height
) - geom
.height
) / 2;
294 simplewindow_init(sw
, phys_screen
, geom
, 0, East
,
295 &globalconf
.colors
.fg
, &globalconf
.colors
.bg
);
297 xcb_map_window(globalconf
.connection
, sw
->window
);
298 mouse_infobox_draw(sw
, geometry
, border
);
300 draw_parser_data_wipe(&pdata
);
303 /** Get the pointer position.
304 * \param window The window to get position on.
305 * \param x will be set to the Pointer-x-coordinate relative to window
306 * \param y will be set to the Pointer-y-coordinate relative to window
307 * \param mask will be set to the current buttons state
308 * \return true on success, false if an error occured
311 mouse_query_pointer(xcb_window_t window
, int *x
, int *y
, uint16_t *mask
)
313 xcb_query_pointer_cookie_t query_ptr_c
;
314 xcb_query_pointer_reply_t
*query_ptr_r
;
316 query_ptr_c
= xcb_query_pointer_unchecked(globalconf
.connection
, window
);
317 query_ptr_r
= xcb_query_pointer_reply(globalconf
.connection
, query_ptr_c
, NULL
);
319 if(!query_ptr_r
|| !query_ptr_r
->same_screen
)
322 *x
= query_ptr_r
->win_x
;
323 *y
= query_ptr_r
->win_y
;
325 *mask
= query_ptr_r
->mask
;
327 p_delete(&query_ptr_r
);
332 /** Get the pointer position on the screen.
333 * \param screen This will be set to the screen number the mouse is on.
334 * \param x This will be set to the Pointer-x-coordinate relative to window.
335 * \param y This will be set to the Pointer-y-coordinate relative to window.
336 * \param mask This will be set to the current buttons state.
337 * \return True on success, false if an error occured.
340 mouse_query_pointer_root(int *s
, int *x
, int *y
, uint16_t *mask
)
343 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
346 xcb_window_t root
= xutil_screen_get(globalconf
.connection
, screen
)->root
;
348 if(mouse_query_pointer(root
, x
, y
, mask
))
357 /** Grab the Pointer.
358 * \param window The window grabbed.
359 * \param cursor The cursor to display (see struct.h CurNormal, CurResize, etc).
360 * \return True on success, false if an error occured.
363 mouse_grab_pointer(xcb_window_t window
, size_t cursor
)
365 xcb_grab_pointer_cookie_t grab_ptr_c
;
366 xcb_grab_pointer_reply_t
*grab_ptr_r
;
368 if(cursor
>= CurLast
)
371 grab_ptr_c
= xcb_grab_pointer_unchecked(globalconf
.connection
, false, window
,
372 MOUSEMASK
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
,
373 window
, globalconf
.cursor
[cursor
], XCB_CURRENT_TIME
);
374 grab_ptr_r
= xcb_grab_pointer_reply(globalconf
.connection
, grab_ptr_c
, NULL
);
379 p_delete(&grab_ptr_r
);
384 /** Ungrab the Pointer
387 mouse_ungrab_pointer(void)
389 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
392 /** Set the pointer position.
393 * \param window The destination window.
394 * \param x X-coordinate inside window.
395 * \param y Y-coordinate inside window.
398 mouse_warp_pointer(xcb_window_t window
, int x
, int y
)
400 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, window
,
404 /** Utility function to help with mouse-dragging
406 * \param x set to x-coordinate of the last event on return
407 * \param y set to y-coordinate of the last event on return
408 * \return true if an motion event was received
409 * false if an button release event was received
412 mouse_track_mouse_drag(int *x
, int *y
)
414 xcb_generic_event_t
*ev
;
415 xcb_motion_notify_event_t
*ev_motion
;
416 xcb_button_release_event_t
*ev_button
;
419 while((ev
= xcb_wait_for_event(globalconf
.connection
)))
420 switch((ev
->response_type
& 0x7F))
423 case XCB_MOTION_NOTIFY
:
424 ev_motion
= (xcb_motion_notify_event_t
*) ev
;
425 *x
= ev_motion
->event_x
;
426 *y
= ev_motion
->event_y
;
430 case XCB_BUTTON_RELEASE
:
431 ev_button
= (xcb_button_release_event_t
*) ev
;
432 *x
= ev_button
->event_x
;
433 *y
= ev_button
->event_y
;
438 xcb_event_handle(&globalconf
.evenths
, ev
);
444 /** Get the client that contains the pointer.
446 * \return The client that contains the pointer or NULL.
449 mouse_get_client_under_pointer(int phys_screen
)
452 xcb_query_pointer_cookie_t query_ptr_c
;
453 xcb_query_pointer_reply_t
*query_ptr_r
;
456 root
= xutil_screen_get(globalconf
.connection
, phys_screen
)->root
;
458 query_ptr_c
= xcb_query_pointer_unchecked(globalconf
.connection
, root
);
459 query_ptr_r
= xcb_query_pointer_reply(globalconf
.connection
, query_ptr_c
, NULL
);
463 c
= client_getbywin(query_ptr_r
->child
);
464 p_delete(&query_ptr_r
);
470 /** Move the focused window with the mouse.
471 * \param c The client.
472 * \param snap The maximum distance in pixels to trigger a "snap".
473 * \param infobox Enable or disable the infobox.
476 mouse_client_move(client_t
*c
, int snap
, bool infobox
)
478 /* current mouse postion */
479 int mouse_x
, mouse_y
;
480 /* last mouse position */
481 int last_x
= 0, last_y
= 0;
486 /* the root window */
489 layout
= layout_get_current(c
->screen
);
490 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
492 /* get current pointer position */
493 mouse_query_pointer(root
, &last_x
, &last_y
, NULL
);
497 || c
->type
== WINDOW_TYPE_DESKTOP
498 || c
->type
== WINDOW_TYPE_SPLASH
499 || c
->type
== WINDOW_TYPE_DOCK
500 || !mouse_grab_pointer(root
, CurMove
))
503 if(infobox
&& (client_isfloating(c
) || layout
== layout_floating
))
504 mouse_infobox_new(&sw
, c
->phys_screen
, c
->border
, c
->geometry
);
508 /* for each motion event */
509 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
510 if(client_isfloating(c
) || layout
== layout_floating
)
514 /* calc new geometry */
515 geometry
= c
->geometry
;
516 geometry
.x
+= (mouse_x
- last_x
);
517 geometry
.y
+= (mouse_y
- last_y
);
520 geometry
= mouse_snapclient(c
, geometry
, snap
);
522 client_resize(c
, geometry
, false);
523 xcb_flush(globalconf
.connection
);
526 /* draw the infobox */
528 mouse_infobox_draw(&sw
, c
->geometry
, c
->border
);
532 xcb_flush(globalconf
.connection
);
543 /* client moved to another screen? */
544 newscreen
= screen_getbycoord(c
->screen
, mouse_x
, mouse_y
);
545 if(newscreen
!= c
->screen
)
547 screen_client_moveto(c
, newscreen
, true, true);
548 globalconf
.screens
[c
->screen
].need_arrange
= true;
549 globalconf
.screens
[newscreen
].need_arrange
= true;
552 xcb_flush(globalconf
.connection
);
555 /* find client to swap with */
556 target
= mouse_get_client_under_pointer(c
->phys_screen
);
559 if(target
&& target
!= c
&& !target
->isfloating
)
561 client_list_swap(&globalconf
.clients
, c
, target
);
562 globalconf
.screens
[c
->screen
].need_arrange
= true;
563 if(globalconf
.hooks
.clients
!= LUA_REFNIL
)
564 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.clients
, 0, 0);
567 xcb_flush(globalconf
.connection
);
572 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
574 /* free the infobox */
576 simplewindow_wipe(&sw
);
580 /** Resize a floating client with the mouse.
581 * \param c The client to resize.
582 * \param corner The corner to resize with.
583 * \param infobox Enable or disable the infobox.
586 mouse_client_resize_floating(client_t
*c
, corner_t corner
, bool infobox
)
588 xcb_screen_t
*screen
;
589 /* one corner of the client has a fixed position */
590 int fixed_x
, fixed_y
;
591 /* the other is moved with the mouse */
592 int mouse_x
= 0, mouse_y
= 0;
595 size_t cursor
= CurResize
;
596 int top
, bottom
, left
, right
;
598 /* do not resize fixed client */
599 if(client_isfixed(c
))
602 screen
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
604 /* get current mouse position */
605 mouse_query_pointer(screen
->root
, &mouse_x
, &mouse_y
, NULL
);
608 bottom
= top
+ c
->geometry
.height
;
609 left
= c
->geometry
.x
;
610 right
= left
+ c
->geometry
.width
;
612 /* figure out which corner to move */
613 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
615 /* the opposite corner is fixed */
616 fixed_x
= (mouse_x
== left
) ? right
: left
;
617 fixed_y
= (mouse_y
== top
) ? bottom
: top
;
626 cursor
= CurTopRight
;
628 case BottomLeftCorner
:
631 case BottomRightCorner
:
632 cursor
= CurBotRight
;
636 /* grab the pointer */
637 if(!mouse_grab_pointer(screen
->root
, cursor
))
640 /* set pointer to the moveable corner */
641 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
643 /* create the infobox */
645 mouse_infobox_new(&sw
, c
->phys_screen
, c
->border
, c
->geometry
);
647 /* for each motion event */
648 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
650 /* new client geometry */
651 area_t geo
= { .x
= MIN(fixed_x
, mouse_x
),
652 .y
= MIN(fixed_y
, mouse_y
),
653 .width
= (MAX(fixed_x
, mouse_x
) - MIN(fixed_x
, mouse_x
)),
654 .height
= (MAX(fixed_y
, mouse_y
) - MIN(fixed_y
, mouse_y
)) };
655 /* new moveable corner */
658 if(mouse_x
== AREA_LEFT(geo
))
659 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopLeftCorner
: BottomLeftCorner
;
661 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopRightCorner
: BottomRightCorner
;
664 if(corner
!= new_corner
)
670 default: cursor
= CurTopLeft
; break;
671 case TopRightCorner
: cursor
= CurTopRight
; break;
672 case BottomLeftCorner
: cursor
= CurBotLeft
; break;
673 case BottomRightCorner
: cursor
= CurBotRight
; break;
676 xcb_change_active_pointer_grab(globalconf
.connection
, globalconf
.cursor
[cursor
],
677 XCB_CURRENT_TIME
, MOUSEMASK
);
680 if(c
->hassizehints
&& c
->honorsizehints
)
684 /* apply size hints */
685 geo
= client_geometry_hints(c
, geo
);
687 /* get the nonmoveable corner back onto fixed_x,fixed_y */
690 default /* TopLeftCorner */:
691 dx
= fixed_x
- AREA_RIGHT(geo
);
692 dy
= fixed_y
- AREA_BOTTOM(geo
);
695 dx
= fixed_x
- AREA_LEFT(geo
);
696 dy
= fixed_y
- AREA_BOTTOM(geo
);
698 case BottomRightCorner
:
699 dx
= fixed_x
- AREA_LEFT(geo
);
700 dy
= fixed_y
- AREA_TOP(geo
);
702 case BottomLeftCorner
:
703 dx
= fixed_x
- AREA_RIGHT(geo
);
704 dy
= fixed_y
- AREA_TOP(geo
);
712 /* resize the client */
713 client_resize(c
, geo
, false);
717 xcb_flush(globalconf
.connection
);
719 /* draw the infobox */
721 mouse_infobox_draw(&sw
, c
->geometry
, c
->border
);
725 mouse_ungrab_pointer();
727 /* free the infobox */
729 simplewindow_wipe(&sw
);
732 /** Resize the master column/row of a tiled layout
733 * \param c A client on the tag/layout to resize.
736 mouse_client_resize_tiled(client_t
*c
)
738 xcb_screen_t
*screen
;
739 /* screen area modulo wibox */
746 int mouse_x
= 0, mouse_y
= 0;
747 size_t cursor
= CurResize
;
749 screen
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
750 tag
= tags_get_current(c
->screen
)[0];
751 layout
= tag
->layout
;
753 area
= screen_area_get(tag
->screen
,
754 &globalconf
.screens
[tag
->screen
].wiboxes
,
755 &globalconf
.screens
[tag
->screen
].padding
,
758 mouse_query_pointer(screen
->root
, &mouse_x
, &mouse_y
, NULL
);
760 /* select initial pointer position */
761 if(layout
== layout_tile
)
763 mouse_x
= area
.x
+ area
.width
* tag
->mwfact
;
766 else if(layout
== layout_tileleft
)
768 mouse_x
= area
.x
+ area
.width
* (1. - tag
->mwfact
);
771 else if(layout
== layout_tilebottom
)
773 mouse_y
= area
.y
+ area
.height
* tag
->mwfact
;
776 else if(layout
== layout_tiletop
)
778 mouse_y
= area
.y
+ area
.height
* (1. - tag
->mwfact
);
784 /* grab the pointer */
785 if(!mouse_grab_pointer(screen
->root
, cursor
))
788 /* set pointer to the moveable border */
789 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
791 xcb_flush(globalconf
.connection
);
793 /* for each motion event */
794 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
796 double mwfact
= 0, fact_x
, fact_y
;
798 /* calculate new master / rest ratio */
799 fact_x
= (double) (mouse_x
- area
.x
) / area
.width
;
800 fact_y
= (double) (mouse_y
- area
.y
) / area
.height
;
802 if(layout
== layout_tile
)
804 else if(layout
== layout_tileleft
)
805 mwfact
= 1. - fact_x
;
806 else if(layout
== layout_tilebottom
)
808 else if(layout
== layout_tiletop
)
809 mwfact
= 1. - fact_y
;
811 /* keep mwfact within reasonable bounds */
812 mwfact
= MIN(MAX( 0.01, mwfact
), 0.99);
815 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
817 tag
->mwfact
= mwfact
;
818 globalconf
.screens
[tag
->screen
].need_arrange
= true;
821 xcb_flush(globalconf
.connection
);
826 mouse_ungrab_pointer();
829 /** Resize the master client in mangifier layout
830 * \param c The client to resize.
831 * \param infobox Enable or disable the infobox.
834 mouse_client_resize_magnified(client_t
*c
, bool infobox
)
836 /* screen area modulo wibox */
839 int center_x
, center_y
;
840 /* max. distance from the center */
843 int mouse_x
= 0, mouse_y
= 0;
844 /* cursor while grabbing */
845 size_t cursor
= CurResize
;
846 corner_t corner
= AutoCorner
;
853 tag
= tags_get_current(c
->screen
)[0];
855 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
857 area
= screen_area_get(tag
->screen
,
858 &globalconf
.screens
[tag
->screen
].wiboxes
,
859 &globalconf
.screens
[tag
->screen
].padding
,
862 center_x
= area
.x
+ (round(area
.width
/ 2.));
863 center_y
= area
.y
+ (round(area
.height
/ 2.));
865 maxdist
= round(sqrt((area
.width
*area
.width
) + (area
.height
*area
.height
)) / 2.);
867 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
869 if(!mouse_query_pointer(root
, &mouse_x
, &mouse_y
, NULL
))
873 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
882 cursor
= CurTopRight
;
884 case BottomLeftCorner
:
887 case BottomRightCorner
:
888 cursor
= CurBotRight
;
893 if(!mouse_grab_pointer(root
, cursor
))
896 /* move pointer to corner */
897 mouse_warp_pointer(root
, mouse_x
, mouse_y
);
899 /* create the infobox */
901 mouse_infobox_new(&sw
, c
->phys_screen
, c
->border
, c
->geometry
);
903 /* for each motion event */
904 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
906 /* \todo keep pointer on screen diagonals */
907 double mwfact
, dist
, dx
, dy
;
909 /* calc distance from the center */
910 dx
= center_x
- mouse_x
;
911 dy
= center_y
- mouse_y
;
912 dist
= sqrt((dx
* dx
) + (dy
* dy
));
914 /* new master/rest ratio */
915 mwfact
= (dist
* dist
) / (maxdist
* maxdist
);
917 /* keep mwfact within reasonable bounds */
918 mwfact
= MIN(MAX( 0.01, mwfact
), 0.99);
920 /* refresh the layout */
921 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
923 tag
->mwfact
= mwfact
;
924 globalconf
.screens
[tag
->screen
].need_arrange
= true;
927 xcb_flush(globalconf
.connection
);
930 /* draw the infobox */
932 mouse_infobox_draw(&sw
, c
->geometry
, c
->border
);
936 mouse_ungrab_pointer();
938 /* free the infobox */
940 simplewindow_wipe(&sw
);
943 /** Resize a client with the mouse.
944 * \param c The client to resize.
945 * \param corner The corner to use.
946 * \param infobox Enable or disable the info box.
949 mouse_client_resize(client_t
*c
, corner_t corner
, bool infobox
)
957 || c
->type
== WINDOW_TYPE_DESKTOP
958 || c
->type
== WINDOW_TYPE_SPLASH
959 || c
->type
== WINDOW_TYPE_DOCK
)
962 curtags
= tags_get_current(c
->screen
);
963 layout
= curtags
[0]->layout
;
964 s
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
966 /* only handle floating, tiled and magnifier layouts */
967 if(layout
== layout_floating
|| client_isfloating(c
))
968 mouse_client_resize_floating(c
, corner
, infobox
);
969 else if(layout
== layout_tile
|| layout
== layout_tileleft
970 || layout
== layout_tilebottom
|| layout
== layout_tiletop
)
973 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
)
974 if(IS_TILED(c
, screen
))
977 /* only masters on this screen? */
978 if(n
<= curtags
[0]->nmaster
)
981 /* no tiled clients on this screen? */
982 for(c
= globalconf
.clients
; c
&& !IS_TILED(c
, screen
); c
= c
->next
);
986 mouse_client_resize_tiled(c
);
988 else if(layout
== layout_magnifier
)
989 mouse_client_resize_magnified(c
, infobox
);
995 /** Resize a client with mouse.
996 * \param L The Lua VM state.
1000 * \lparam An optional table with keys: `corner', such as bottomleft,
1001 * topright, etc, to specify which corner to grab (default to auto) and
1002 * `infobox' to enable or disable the coordinates and dimensions box (default to
1006 luaA_client_mouse_resize(lua_State
*L
)
1008 client_t
**c
= luaA_checkudata(L
, 1, "client");
1009 corner_t corner
= AutoCorner
;
1010 bool infobox
= true;
1014 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
1016 luaA_checktable(L
, 2);
1017 buf
= luaA_getopt_lstring(L
, 2, "corner", "auto", &len
);
1018 corner
= a_strtocorner(buf
, len
);
1019 infobox
= luaA_getopt_boolean(L
, 2, "infobox", true);
1022 mouse_client_resize(*c
, corner
, infobox
);
1027 /** Move a client with mouse.
1028 * \param L The Lua VM state.
1032 * \lparam An optional table with keys: `snap' for pixel to snap (default to 8), and
1033 * `infobox' to enable or disable the coordinates and dimensions box (default to
1037 luaA_client_mouse_move(lua_State
*L
)
1039 client_t
**c
= luaA_checkudata(L
, 1, "client");
1041 bool infobox
= true;
1043 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
1045 luaA_checktable(L
, 2);
1046 snap
= luaA_getopt_number(L
, 2, "snap", 8);
1047 infobox
= luaA_getopt_boolean(L
, 2, "infobox", true);
1050 mouse_client_move(*c
, snap
, infobox
);
1055 /** Create a new mouse button bindings.
1056 * \param L The Lua VM state.
1057 * \return The number of elements pushed on stack.
1059 * \lparam A table with modifiers keys, or a button to clone.
1060 * \lparam A mouse button number.
1061 * \lparam A function to execute on click events.
1062 * \lparam A function to execute on release events.
1063 * \lreturn A mouse button binding.
1066 luaA_button_new(lua_State
*L
)
1069 button_t
*button
, **orig
;
1071 if((orig
= luaA_toudata(L
, 2, "button")))
1073 button_t
*copy
= p_new(button_t
, 1);
1074 copy
->mod
= (*orig
)->mod
;
1075 copy
->button
= (*orig
)->button
;
1076 if((*orig
)->press
!= LUA_REFNIL
)
1078 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*orig
)->press
);
1079 luaA_registerfct(L
, -1, ©
->press
);
1082 copy
->press
= LUA_REFNIL
;
1083 if((*orig
)->release
!= LUA_REFNIL
)
1085 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*orig
)->release
);
1086 luaA_registerfct(L
, -1, ©
->release
);
1089 copy
->release
= LUA_REFNIL
;
1090 return luaA_button_userdata_new(L
, copy
);
1093 luaA_checktable(L
, 2);
1094 /* arg 3 is mouse button */
1095 i
= luaL_checknumber(L
, 3);
1096 /* arg 4 and 5 are callback functions */
1097 if(!lua_isnil(L
, 4))
1098 luaA_checkfunction(L
, 4);
1099 if(lua_gettop(L
) == 5 && !lua_isnil(L
, 5))
1100 luaA_checkfunction(L
, 5);
1102 button
= p_new(button_t
, 1);
1103 button
->button
= xutil_button_fromint(i
);
1106 button
->press
= LUA_REFNIL
;
1108 luaA_registerfct(L
, 4, &button
->press
);
1110 if(lua_gettop(L
) == 5 && !lua_isnil(L
, 5))
1111 luaA_registerfct(L
, 5, &button
->release
);
1113 button
->release
= LUA_REFNIL
;
1115 len
= lua_objlen(L
, 2);
1116 for(i
= 1; i
<= len
; i
++)
1120 lua_rawgeti(L
, 2, i
);
1121 buf
= luaL_checklstring(L
, -1, &blen
);
1122 button
->mod
|= xutil_key_mask_fromstr(buf
, blen
);
1125 return luaA_button_userdata_new(L
, button
);
1128 /** Set a button array with a Lua table.
1129 * \param L The Lua VM state.
1130 * \param idx The index of the Lua table.
1131 * \param buttons The array button to fill.
1134 luaA_button_array_set(lua_State
*L
, int idx
, button_array_t
*buttons
)
1138 luaA_checktable(L
, idx
);
1139 button_array_wipe(buttons
);
1140 button_array_init(buttons
);
1142 while(lua_next(L
, idx
))
1144 b
= luaA_checkudata(L
, -1, "button");
1145 button_array_append(buttons
, *b
);
1151 /** Push an array of button as an Lua table onto the stack.
1152 * \param L The Lua VM state.
1153 * \param buttons The button array to push.
1154 * \return The number of elements pushed on stack.
1157 luaA_button_array_get(lua_State
*L
, button_array_t
*buttons
)
1160 for(int i
= 0; i
< buttons
->len
; i
++)
1162 luaA_button_userdata_new(L
, buttons
->tab
[i
]);
1163 lua_rawseti(L
, -2, i
+ 1);
1169 * \param L The Lua VM state.
1170 * \return The number of elements pushed on stack.
1172 * \lfield press The function called when button press event is received.
1173 * \lfield release The function called when button release event is received.
1176 luaA_button_index(lua_State
*L
)
1178 if(luaA_usemetatable(L
, 1, 2))
1182 button_t
**button
= luaA_checkudata(L
, 1, "button");
1183 const char *attr
= luaL_checklstring(L
, 2, &len
);
1185 switch(a_tokenize(attr
, len
))
1188 if((*button
)->press
!= LUA_REFNIL
)
1189 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*button
)->press
);
1194 if((*button
)->release
!= LUA_REFNIL
)
1195 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*button
)->release
);
1200 /* works fine, but not *really* neat */
1201 lua_pushnumber(L
, (*button
)->button
);
1211 * \param L The Lua VM state.
1212 * \return The number of elements pushed on stack.
1216 luaA_button_newindex(lua_State
*L
)
1218 if(luaA_usemetatable(L
, 1, 2))
1222 button_t
**button
= luaA_checkudata(L
, 1, "button");
1223 const char *attr
= luaL_checklstring(L
, 2, &len
);
1225 switch(a_tokenize(attr
, len
))
1228 luaA_registerfct(L
, 3, &(*button
)->press
);
1231 luaA_registerfct(L
, 3, &(*button
)->release
);
1234 (*button
)->button
= xutil_button_fromint(luaL_checknumber(L
, 3));
1243 const struct luaL_reg awesome_button_methods
[] =
1245 { "__call", luaA_button_new
},
1248 const struct luaL_reg awesome_button_meta
[] =
1250 { "__index", luaA_button_index
},
1251 { "__newindex", luaA_button_newindex
},
1252 { "__gc", luaA_button_gc
},
1253 { "__eq", luaA_button_eq
},
1254 { "__tostring", luaA_button_tostring
},
1259 * \param L The Lua VM state.
1260 * \return The number of elements pushed on stack.
1262 * \lfield coords Mouse coordinates.
1263 * \lfield screen Mouse screen number.
1266 luaA_mouse_index(lua_State
*L
)
1269 const char *attr
= luaL_checklstring(L
, 2, &len
);
1270 int mouse_x
, mouse_y
, i
;
1273 switch(a_tokenize(attr
, len
))
1276 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, NULL
))
1279 i
= screen_getbycoord(screen
, mouse_x
, mouse_y
);
1281 lua_pushnumber(L
, i
+ 1);
1290 /** Newindex for mouse.
1291 * \param L The Lua VM state.
1292 * \return The number of elements pushed on stack.
1295 luaA_mouse_newindex(lua_State
*L
)
1298 const char *attr
= luaL_checklstring(L
, 2, &len
);
1301 int screen
, phys_screen
;
1303 switch(a_tokenize(attr
, len
))
1306 screen
= luaL_checknumber(L
, 3) - 1;
1307 luaA_checkscreen(screen
);
1309 /* we need the physical one to get the root window */
1310 phys_screen
= screen_virttophys(screen
);
1311 root
= xutil_screen_get(globalconf
.connection
, phys_screen
)->root
;
1313 x
= globalconf
.screens
[screen
].geometry
.x
;
1314 y
= globalconf
.screens
[screen
].geometry
.y
;
1316 mouse_warp_pointer(root
, x
, y
);
1325 /** Get or set the mouse coords.
1326 * \param L The Lua VM state.
1327 * \return The number of elements pushed on stack.
1329 * \lparam None or a table with x and y keys as mouse coordinates.
1330 * \lreturn A table with mouse coordinates.
1333 luaA_mouse_coords(lua_State
*L
)
1335 uint16_t mask
, maski
;
1336 int screen
, x
, y
, mouse_x
, mouse_y
, i
= 1;
1338 if(lua_gettop(L
) == 1)
1342 luaA_checktable(L
, 1);
1344 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, &mask
))
1347 x
= luaA_getopt_number(L
, 1, "x", mouse_x
);
1348 y
= luaA_getopt_number(L
, 1, "y", mouse_y
);
1350 root
= xutil_screen_get(globalconf
.connection
, screen
)->root
;
1351 mouse_warp_pointer(root
, x
, y
);
1355 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, &mask
))
1359 lua_pushnumber(L
, mouse_x
);
1360 lua_setfield(L
, -2, "x");
1361 lua_pushnumber(L
, mouse_y
);
1362 lua_setfield(L
, -2, "y");
1364 for(maski
= XCB_BUTTON_MASK_1
; i
<= XCB_BUTTON_MASK_5
; maski
<<= 1, i
++)
1367 lua_pushboolean(L
, true);
1368 lua_rawseti(L
, -2, i
);
1370 lua_setfield(L
, -2, "buttons");
1376 * \param L The Lua VM state.
1377 * \return The number of elements pushed on stack.
1380 luaA_mouse_new(lua_State
*L
)
1382 deprecate(L
, "button()");
1383 return luaA_button_new(L
);
1386 const struct luaL_reg awesome_mouse_methods
[] =
1389 { "__call", luaA_mouse_new
},
1390 { "__index", luaA_mouse_index
},
1391 { "__newindex", luaA_mouse_newindex
},
1392 { "coords", luaA_mouse_coords
},
1395 const struct luaL_reg awesome_mouse_meta
[] =
1400 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80