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"
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. */
54 /** Convert a corner name into a corner type.
55 * \param str A string.
56 * \param len String length.
57 * \return A corner type.
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
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
;
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
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
))
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
))
116 geometry
.y
= snap_geometry
.y
+ snap_geometry
.height
- geometry
.height
;
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.
128 mouse_snapclient(client_t
*c
, area_t geometry
, int snap
)
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 geometry
= titlebar_geometry_add(c
->titlebar
, c
->border
, geometry
);
138 geometry
.width
+= 2 * c
->border
;
139 geometry
.height
+= 2 * c
->border
;
142 mouse_snapclienttogeometry_inside(geometry
, screen_geometry
, snap
);
144 for(snapper
= globalconf
.clients
; snapper
; snapper
= snapper
->next
)
145 if(snapper
!= c
&& client_isvisible(c
, c
->screen
))
147 snapper_geometry
= snapper
->geometry
;
148 snapper_geometry
.width
+= 2 * c
->border
;
149 snapper_geometry
.height
+= 2 * c
->border
;
150 snapper_geometry
= titlebar_geometry_add(c
->titlebar
, c
->border
, snapper_geometry
);
152 mouse_snapclienttogeometry_outside(geometry
,
157 geometry
.width
-= 2 * c
->border
;
158 geometry
.height
-= 2 * c
->border
;
159 return titlebar_geometry_remove(c
->titlebar
, c
->border
, geometry
);
162 /** Set coordinates to a corner of an area.
165 * \param[in,out] x The x coordinate.
166 * \param[in,out] y The y coordinate.
167 * \param corner The corner to snap to.
168 * \return The corner the coordinates have been set to. If corner != AutoCorner
169 * this is always equal to \em corner.
171 * \todo rename/move this is still awkward and might be useful somewhere else.
174 mouse_snap_to_corner(area_t a
, int *x
, int *y
, corner_t corner
)
176 int top
, bottom
, left
, right
;
179 bottom
= AREA_BOTTOM(a
);
181 right
= AREA_RIGHT(a
);
183 /* figure out the nearser corner */
184 if(corner
== AutoCorner
)
186 if(abs(top
- *y
) < abs(bottom
- *y
))
188 if(abs(left
- *x
) < abs(right
- *x
))
189 corner
= TopLeftCorner
;
191 corner
= TopRightCorner
;
195 if(abs(left
- *x
) < abs(right
- *x
))
196 corner
= BottomLeftCorner
;
198 corner
= BottomRightCorner
;
214 case BottomLeftCorner
:
219 case BottomRightCorner
:
231 /** Redraw the infobox.
232 * \param ctx Draw context.
233 * \param sw The simple window.
234 * \param geometry The geometry to use for the box.
235 * \param border The client border size.
238 mouse_infobox_draw(draw_context_t
*ctx
,
240 area_t geometry
, int border
)
242 area_t draw_geometry
= { 0, 0, ctx
->width
, ctx
->height
};
246 len
= snprintf(size
, sizeof(size
), "<text align=\"center\"/>%dx%d+%d+%d",
247 geometry
.width
, geometry
.height
, geometry
.x
, geometry
.y
);
248 draw_rectangle(ctx
, draw_geometry
, 1.0, true, &globalconf
.colors
.bg
);
249 draw_text(ctx
, globalconf
.font
, draw_geometry
, size
, len
, NULL
);
250 simplewindow_move(sw
,
251 geometry
.x
+ ((2 * border
+ geometry
.width
) - sw
->geometry
.width
) / 2,
252 geometry
.y
+ ((2 * border
+ geometry
.height
) - sw
->geometry
.height
) / 2);
253 simplewindow_refresh_pixmap(sw
);
254 xcb_aux_sync(ctx
->connection
);
257 #define MOUSE_INFOBOX_STRING_DEFAULT "0000x0000+0000+0000"
259 /** Initialize the infobox window.
260 * \param phys_screen Physical screen number.
261 * \param border Border size of the client.
262 * \param geometry Client geometry.
263 * \param ctx Draw context to create.
264 * \return The simple window.
266 static simple_window_t
*
267 mouse_infobox_new(int phys_screen
, int border
, area_t geometry
,
268 draw_context_t
**ctx
)
272 draw_parser_data_t pdata
;
274 draw_parser_data_init(&pdata
);
276 geom
= draw_text_extents(globalconf
.connection
,
277 globalconf
.default_screen
,
279 MOUSE_INFOBOX_STRING_DEFAULT
,
280 sizeof(MOUSE_INFOBOX_STRING_DEFAULT
)-1,
282 geom
.x
= geometry
.x
+ ((2 * border
+ geometry
.width
) - geom
.width
) / 2;
283 geom
.y
= geometry
.y
+ ((2 * border
+ geometry
.height
) - geom
.height
) / 2;
285 sw
= simplewindow_new(globalconf
.connection
, phys_screen
,
287 geom
.width
, geom
.height
, 0);
289 *ctx
= draw_context_new(globalconf
.connection
, sw
->phys_screen
,
290 sw
->geometry
.width
, sw
->geometry
.height
,
292 &globalconf
.colors
.fg
,
293 &globalconf
.colors
.bg
);
295 xcb_map_window(globalconf
.connection
, sw
->window
);
296 mouse_infobox_draw(*ctx
, sw
, geometry
, border
);
298 draw_parser_data_wipe(&pdata
);
303 /** Get the Pointer position
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
);
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 /** Grab the Pointer.
333 * \param window The window grabbed.
334 * \param cursor The cursor to display (see struct.h CurNormal, CurResize, etc).
335 * \return True on success, false if an error occured.
338 mouse_grab_pointer(xcb_window_t window
, size_t cursor
)
340 xcb_grab_pointer_cookie_t grab_ptr_c
;
341 xcb_grab_pointer_reply_t
*grab_ptr_r
;
343 if(cursor
>= CurLast
)
346 grab_ptr_c
= xcb_grab_pointer_unchecked(globalconf
.connection
, false, window
,
347 MOUSEMASK
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
,
348 window
, globalconf
.cursor
[cursor
], XCB_CURRENT_TIME
);
349 grab_ptr_r
= xcb_grab_pointer_reply(globalconf
.connection
, grab_ptr_c
, NULL
);
354 p_delete(&grab_ptr_r
);
359 /** Ungrab the Pointer
362 mouse_ungrab_pointer(void)
364 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
367 /** Set the Pointer position
368 * \param window the destination window
369 * \param x x-coordinate inside window
370 * \param y y-coordinate inside window
373 mouse_warp_pointer(xcb_window_t window
, int x
, int y
)
375 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, window
,
379 /** Utility function to help with mouse-dragging
381 * \param x set to x-coordinate of the last event on return
382 * \param y set to y-coordinate of the last event on return
383 * \return true if an motion event was received
384 * false if an button release event was received
387 mouse_track_mouse_drag(int *x
, int *y
)
389 xcb_generic_event_t
*ev
;
390 xcb_motion_notify_event_t
*ev_motion
;
391 xcb_button_release_event_t
*ev_button
;
395 while((ev
= xcb_wait_for_event(globalconf
.connection
)))
397 switch((ev
->response_type
& 0x7F))
400 case XCB_MOTION_NOTIFY
:
401 ev_motion
= (xcb_motion_notify_event_t
*) ev
;
402 *x
= ev_motion
->event_x
;
403 *y
= ev_motion
->event_y
;
407 case XCB_BUTTON_RELEASE
:
408 ev_button
= (xcb_button_release_event_t
*) ev
;
409 *x
= ev_button
->event_x
;
410 *y
= ev_button
->event_y
;
415 xcb_handle_event(globalconf
.evenths
, ev
);
424 /** Get the client that contains the Pointer.
426 * \return The client that contains the Pointer or NULL.
429 mouse_get_client_under_pointer(void)
432 xcb_query_pointer_cookie_t query_ptr_c
;
433 xcb_query_pointer_reply_t
*query_ptr_r
;
436 root
= xutil_screen_get(globalconf
.connection
, globalconf
.default_screen
)->root
;
438 query_ptr_c
= xcb_query_pointer_unchecked(globalconf
.connection
, root
);
439 query_ptr_r
= xcb_query_pointer_reply(globalconf
.connection
, query_ptr_c
, NULL
);
443 c
= client_getbywin(query_ptr_r
->child
);
444 p_delete(&query_ptr_r
);
450 /** Move the focused window with the mouse.
451 * \param c The client.
452 * \param snap The maximum distance in pixels to trigger a "snap".
453 * \param infobox Enable or disable the infobox.
456 mouse_client_move(client_t
*c
, int snap
, bool infobox
)
458 /* current mouse postion */
459 int mouse_x
, mouse_y
;
460 /* last mouse position */
461 int last_x
= 0, last_y
= 0;
465 simple_window_t
*sw
= NULL
;
467 /* the root window */
470 layout
= layout_get_current(c
->screen
);
471 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
473 /* get current pointer position */
474 mouse_query_pointer(root
, &last_x
, &last_y
, NULL
);
477 if(!mouse_grab_pointer(root
, CurMove
))
482 if(infobox
&& (c
->isfloating
|| layout
== layout_floating
))
483 sw
= mouse_infobox_new(c
->phys_screen
, c
->border
, c
->geometry
, &ctx
);
485 /* for each motion event */
486 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
487 if(c
->isfloating
|| layout
== layout_floating
)
491 /* calc new geometry */
492 geometry
= c
->geometry
;
493 geometry
.x
+= (mouse_x
- last_x
);
494 geometry
.y
+= (mouse_y
- last_y
);
497 geometry
= mouse_snapclient(c
, geometry
, snap
);
499 client_resize(c
, geometry
, false);
502 /* draw the infobox */
504 mouse_infobox_draw(ctx
, sw
, c
->geometry
, c
->border
);
515 /* client moved to another screen? */
516 newscreen
= screen_get_bycoord(globalconf
.screens_info
, c
->screen
,
518 if(newscreen
!= c
->screen
)
520 screen_client_moveto(c
, newscreen
, true);
521 globalconf
.screens
[c
->screen
].need_arrange
= true;
522 globalconf
.screens
[newscreen
].need_arrange
= true;
527 /* find client to swap with */
528 target
= mouse_get_client_under_pointer();
531 if(target
&& target
!= c
&& !target
->isfloating
)
533 client_list_swap(&globalconf
.clients
, c
, target
);
534 globalconf
.screens
[c
->screen
].need_arrange
= true;
541 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
543 /* free the infobox */
546 draw_context_delete(&ctx
);
547 simplewindow_delete(&sw
);
552 /** Resize a floating client with the mouse.
553 * \param c The client to resize.
554 * \param corner The corner to resize with.
555 * \param infobox Enable or disable the infobox.
558 mouse_client_resize_floating(client_t
*c
, corner_t corner
, bool infobox
)
560 xcb_screen_t
*screen
;
561 /* one corner of the client has a fixed position */
562 int fixed_x
, fixed_y
;
563 /* the other is moved with the mouse */
564 int mouse_x
= 0, mouse_y
= 0;
566 simple_window_t
*sw
= NULL
;
568 size_t cursor
= CurResize
;
569 int top
, bottom
, left
, right
;
571 screen
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
573 /* get current mouse position */
574 mouse_query_pointer(screen
->root
, &mouse_x
, &mouse_y
, NULL
);
577 bottom
= top
+ c
->geometry
.height
;
578 left
= c
->geometry
.x
;
579 right
= left
+ c
->geometry
.width
;
581 /* figure out which corner to move */
582 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
584 /* the opposite corner is fixed */
585 fixed_x
= (mouse_x
== left
) ? right
: left
;
586 fixed_y
= (mouse_y
== top
) ? bottom
: top
;
595 cursor
= CurTopRight
;
597 case BottomLeftCorner
:
600 case BottomRightCorner
:
601 cursor
= CurBotRight
;
605 /* grab the pointer */
606 if(!mouse_grab_pointer(screen
->root
, cursor
))
609 /* set pointer to the moveable corner */
610 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
612 /* create the infobox */
614 sw
= mouse_infobox_new(c
->phys_screen
, c
->border
, c
->geometry
, &ctx
);
616 /* for each motion event */
617 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
619 /* new client geometry */
620 area_t geo
= { .x
= MIN(fixed_x
, mouse_x
),
621 .y
= MIN(fixed_y
, mouse_y
),
622 .width
= (MAX(fixed_x
, mouse_x
) - MIN(fixed_x
, mouse_x
)),
623 .height
= (MAX(fixed_y
, mouse_y
) - MIN(fixed_y
, mouse_y
)) };
624 /* new moveable corner */
627 if(mouse_x
== AREA_LEFT(geo
))
628 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopLeftCorner
: BottomLeftCorner
;
630 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopRightCorner
: BottomRightCorner
;
633 if(corner
!= new_corner
)
639 default: cursor
= CurTopLeft
; break;
640 case TopRightCorner
: cursor
= CurTopRight
; break;
641 case BottomLeftCorner
: cursor
= CurBotLeft
; break;
642 case BottomRightCorner
: cursor
= CurBotRight
; break;
645 xcb_change_active_pointer_grab(globalconf
.connection
, globalconf
.cursor
[cursor
],
646 XCB_CURRENT_TIME
, MOUSEMASK
);
649 if(c
->hassizehints
&& c
->honorsizehints
)
653 /* apply size hints */
654 geo
= client_geometry_hints(c
, geo
);
656 /* get the nonmoveable corner back onto fixed_x,fixed_y */
659 default /* TopLeftCorner */:
660 dx
= fixed_x
- AREA_RIGHT(geo
);
661 dy
= fixed_y
- AREA_BOTTOM(geo
);
664 dx
= fixed_x
- AREA_LEFT(geo
);
665 dy
= fixed_y
- AREA_BOTTOM(geo
);
667 case BottomRightCorner
:
668 dx
= fixed_x
- AREA_LEFT(geo
);
669 dy
= fixed_y
- AREA_TOP(geo
);
671 case BottomLeftCorner
:
672 dx
= fixed_x
- AREA_RIGHT(geo
);
673 dy
= fixed_y
- AREA_TOP(geo
);
681 /* resize the client */
682 client_resize(c
, geo
, false);
683 /* redaw titlebar live */
686 /* draw the infobox */
688 mouse_infobox_draw(ctx
, sw
, c
->geometry
, c
->border
);
692 mouse_ungrab_pointer();
694 /* free the infobox */
697 draw_context_delete(&ctx
);
698 simplewindow_delete(&sw
);
702 /** Resize the master column/row of a tiled layout
703 * \param c A client on the tag/layout to resize.
706 mouse_client_resize_tiled(client_t
*c
)
708 xcb_screen_t
*screen
;
709 /* screen area modulo statusbar */
716 int mouse_x
= 0, mouse_y
= 0;
717 size_t cursor
= CurResize
;
719 screen
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
720 tag
= tags_get_current(c
->screen
)[0];
721 layout
= tag
->layout
;
723 area
= screen_area_get(&globalconf
.screens
[tag
->screen
].geometry
,
724 globalconf
.screens
[tag
->screen
].statusbar
,
725 &globalconf
.screens
[tag
->screen
].padding
);
727 mouse_query_pointer(screen
->root
, &mouse_x
, &mouse_y
, NULL
);
729 /* select initial pointer position */
730 if(layout
== layout_tile
)
732 mouse_x
= area
.x
+ area
.width
* tag
->mwfact
;
735 else if(layout
== layout_tileleft
)
737 mouse_x
= area
.x
+ area
.width
* (1. - tag
->mwfact
);
740 else if(layout
== layout_tilebottom
)
742 mouse_y
= area
.y
+ area
.height
* tag
->mwfact
;
745 else if(layout
== layout_tiletop
)
747 mouse_y
= area
.y
+ area
.height
* (1. - tag
->mwfact
);
753 /* grab the pointer */
754 if(!mouse_grab_pointer(screen
->root
, cursor
))
757 /* set pointer to the moveable border */
758 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
760 xcb_aux_sync(globalconf
.connection
);
762 /* for each motion event */
763 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
765 double mwfact
= 0, fact_x
, fact_y
;
767 /* calculate new master / rest ratio */
768 fact_x
= (double) (mouse_x
- area
.x
) / area
.width
;
769 fact_y
= (double) (mouse_y
- area
.y
) / area
.height
;
771 if(layout
== layout_tile
)
773 else if(layout
== layout_tileleft
)
774 mwfact
= 1. - fact_x
;
775 else if(layout
== layout_tilebottom
)
777 else if(layout
== layout_tiletop
)
778 mwfact
= 1. - fact_y
;
780 /* keep mwfact within reasonable bounds */
781 mwfact
= MIN(MAX( 0.01, mwfact
), 0.99);
784 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
786 tag
->mwfact
= mwfact
;
787 globalconf
.screens
[tag
->screen
].need_arrange
= true;
794 mouse_ungrab_pointer();
797 /** Resize the master client in mangifier layout
798 * \param c The client to resize.
799 * \param infobox Enable or disable the infobox.
802 mouse_client_resize_magnified(client_t
*c
, bool infobox
)
804 /* screen area modulo statusbar */
807 int center_x
, center_y
;
808 /* max. distance from the center */
811 int mouse_x
= 0, mouse_y
= 0;
812 /* cursor while grabbing */
813 size_t cursor
= CurResize
;
814 corner_t corner
= AutoCorner
;
818 simple_window_t
*sw
= NULL
;
822 tag
= tags_get_current(c
->screen
)[0];
824 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
826 area
= screen_area_get(&globalconf
.screens
[tag
->screen
].geometry
,
827 globalconf
.screens
[tag
->screen
].statusbar
,
828 &globalconf
.screens
[tag
->screen
].padding
);
830 center_x
= area
.x
+ (round(area
.width
/ 2.));
831 center_y
= area
.y
+ (round(area
.height
/ 2.));
833 maxdist
= round(sqrt((area
.width
*area
.width
) + (area
.height
*area
.height
)) / 2.);
835 /* get current mouse position */
836 mouse_query_pointer(root
, &mouse_x
, &mouse_y
, NULL
);
839 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
848 cursor
= CurTopRight
;
850 case BottomLeftCorner
:
853 case BottomRightCorner
:
854 cursor
= CurBotRight
;
859 if(!mouse_grab_pointer(root
, cursor
))
862 /* move pointer to corner */
863 mouse_warp_pointer(root
, mouse_x
, mouse_y
);
865 /* create the infobox */
867 sw
= mouse_infobox_new(c
->phys_screen
, c
->border
, c
->geometry
, &ctx
);
869 /* for each motion event */
870 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
872 /* \todo keep pointer on screen diagonals */
873 double mwfact
, dist
, dx
, dy
;
875 /* calc distance from the center */
876 dx
= center_x
- mouse_x
;
877 dy
= center_y
- mouse_y
;
878 dist
= sqrt((dx
*dx
) + (dy
*dy
));
880 /* new master/rest ratio */
881 mwfact
= dist
/ maxdist
;
883 /* keep mwfact within reasonable bounds */
884 mwfact
= MIN(MAX( 0.01, mwfact
), 0.99);
886 /* refresh the layout */
887 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
889 tag
->mwfact
= mwfact
;
890 globalconf
.screens
[tag
->screen
].need_arrange
= true;
895 /* draw the infobox */
897 mouse_infobox_draw(ctx
, sw
, c
->geometry
, c
->border
);
901 mouse_ungrab_pointer();
903 /* free the infobox */
906 draw_context_delete(&ctx
);
907 simplewindow_delete(&sw
);
911 /** Resize a client with the mouse.
912 * \param c The client to resize.
913 * \param corner The corner to use.
914 * \param infobox Enable or disable the info box.
917 mouse_client_resize(client_t
*c
, corner_t corner
, bool infobox
)
924 curtags
= tags_get_current(c
->screen
);
925 layout
= curtags
[0]->layout
;
926 s
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
928 /* only handle floating, tiled and magnifier layouts */
929 if(layout
== layout_floating
|| c
->isfloating
)
936 mouse_client_resize_floating(c
, corner
, infobox
);
938 else if(layout
== layout_tile
|| layout
== layout_tileleft
939 || layout
== layout_tilebottom
|| layout
== layout_tiletop
)
942 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
)
943 if(IS_TILED(c
, screen
))
946 /* only masters on this screen? */
947 if(n
<= curtags
[0]->nmaster
)
950 /* no tiled clients on this screen? */
951 for(c
= globalconf
.clients
; c
&& !IS_TILED(c
, screen
); c
= c
->next
);
955 mouse_client_resize_tiled(c
);
957 else if(layout
== layout_magnifier
)
958 mouse_client_resize_magnified(c
, infobox
);
964 /** Resize a client with mouse.
965 * \param L The Lua VM state.
969 * \lparam An optional table with keys: `corner', such as bottomleft,
970 * topright, etc, to specify which corner to grab (default to auto) and
971 * `infobox' to enable or disable the coordinates and dimensions box (default to
975 luaA_client_mouse_resize(lua_State
*L
)
977 client_t
**c
= luaA_checkudata(L
, 1, "client");
978 corner_t corner
= AutoCorner
;
983 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
985 luaA_checktable(L
, 2);
986 buf
= luaA_getopt_lstring(L
, 2, "corner", "auto", &len
);
987 corner
= a_strtocorner(buf
, len
);
988 infobox
= luaA_getopt_boolean(L
, 2, "infobox", true);
991 mouse_client_resize(*c
, corner
, infobox
);
996 /** Move a client with mouse.
997 * \param L The Lua VM state.
1001 * \lparam An optional table with keys: `snap' for pixel to snap (default to 8), and
1002 * `infobox' to enable or disable the coordinates and dimensions box (default to
1006 luaA_client_mouse_move(lua_State
*L
)
1008 client_t
**c
= luaA_checkudata(L
, 1, "client");
1010 bool infobox
= true;
1012 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
1014 luaA_checktable(L
, 2);
1015 snap
= luaA_getopt_number(L
, 2, "snap", 8);
1016 infobox
= luaA_getopt_boolean(L
, 2, "infobox", true);
1019 mouse_client_move(*c
, snap
, infobox
);
1024 /** Create a new mouse button bindings.
1025 * \param L The Lua VM state.
1026 * \return The number of elements pushed on stack.
1028 * \lparam A table with modifiers keys.
1029 * \lparam A mouse button number.
1030 * \lparam A function to execute on click events.
1031 * \lreturn A mouse button binding.
1034 luaA_mouse_new(lua_State
*L
)
1039 luaA_checktable(L
, 2);
1040 /* arg 3 is mouse button */
1041 i
= luaL_checknumber(L
, 3);
1042 /* arg 4 is cmd to run */
1043 luaA_checkfunction(L
, 4);
1045 button
= p_new(button_t
, 1);
1046 button
->button
= xutil_button_fromint(i
);
1047 button
->fct
= luaL_ref(L
, LUA_REGISTRYINDEX
);
1049 len
= lua_objlen(L
, 2);
1050 for(i
= 1; i
<= len
; i
++)
1052 lua_rawgeti(L
, 2, i
);
1053 button
->mod
|= xutil_key_mask_fromstr(luaL_checkstring(L
, -1));
1056 return luaA_mouse_userdata_new(L
, button
);
1060 * \param L The Lua VM state.
1061 * \return The number of elements pushed on stack.
1063 * \lfield coords Mouse coordinates.
1064 * \lfield screen Mouse screen number.
1067 luaA_mouse_index(lua_State
*L
)
1070 const char *attr
= luaL_checklstring(L
, 2, &len
);
1071 int mouse_x
, mouse_y
, i
= 0;
1075 switch(a_tokenize(attr
, len
))
1078 root
= xutil_screen_get(globalconf
.connection
, globalconf
.default_screen
)->root
;
1080 if(!mouse_query_pointer(root
, &mouse_x
, &mouse_y
, &mask
))
1084 lua_pushnumber(L
, mouse_x
);
1085 lua_setfield(L
, -2, "x");
1086 lua_pushnumber(L
, mouse_y
);
1087 lua_setfield(L
, -2, "y");
1089 if (mask
& XCB_BUTTON_MASK_1
)
1091 lua_pushnumber(L
, 1);
1092 lua_rawseti(L
, -2, ++i
);
1094 if (mask
& XCB_BUTTON_MASK_2
)
1096 lua_pushnumber(L
, 2);
1097 lua_rawseti(L
, -2, ++i
);
1099 if (mask
& XCB_BUTTON_MASK_3
)
1101 lua_pushnumber(L
, 3);
1102 lua_rawseti(L
, -2, ++i
);
1104 if (mask
& XCB_BUTTON_MASK_4
)
1106 lua_pushnumber(L
, 4);
1107 lua_rawseti(L
, -2, ++i
);
1109 if (mask
& XCB_BUTTON_MASK_5
)
1111 lua_pushnumber(L
, 5);
1112 lua_rawseti(L
, -2, ++i
);
1114 lua_setfield(L
, -2, "buttons");
1117 root
= xutil_screen_get(globalconf
.connection
, globalconf
.default_screen
)->root
;
1119 if(!mouse_query_pointer(root
, &mouse_x
, &mouse_y
, NULL
))
1122 i
= screen_get_bycoord(globalconf
.screens_info
,
1123 globalconf
.default_screen
,
1126 lua_pushnumber(L
, i
+ 1);
1135 /** Newindex for mouse.
1136 * \param L The Lua VM state.
1137 * \return The number of elements pushed on stack.
1140 luaA_mouse_newindex(lua_State
*L
)
1143 const char *attr
= luaL_checklstring(L
, 2, &len
);
1144 int mouse_x
, mouse_y
, x
, y
= 0;
1148 switch(a_tokenize(attr
, len
))
1151 luaA_checktable(L
, 3);
1153 root
= xutil_screen_get(globalconf
.connection
, globalconf
.default_screen
)->root
;
1154 if(!mouse_query_pointer(root
, &mouse_x
, &mouse_y
, &mask
))
1157 x
= luaA_getopt_number(L
, 3, "x", mouse_x
);
1158 y
= luaA_getopt_number(L
, 3, "y", mouse_y
);
1160 mouse_warp_pointer(root
, x
, y
);
1169 const struct luaL_reg awesome_mouse_methods
[] =
1171 { "__call", luaA_mouse_new
},
1172 { "__index", luaA_mouse_index
},
1173 { "__newindex", luaA_mouse_newindex
},
1176 const struct luaL_reg awesome_mouse_meta
[] =
1178 { "__gc", luaA_mouse_gc
},
1179 { "__eq", luaA_mouse_eq
},
1183 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80