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(c
->screen
,
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;
526 /* find client to swap with */
527 target
= mouse_get_client_under_pointer();
530 if(target
&& target
!= c
&& !target
->isfloating
)
532 client_list_swap(&globalconf
.clients
, c
, target
);
533 globalconf
.screens
[c
->screen
].need_arrange
= true;
539 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
541 /* free the infobox */
544 draw_context_delete(&ctx
);
545 simplewindow_delete(&sw
);
550 /** Resize a floating client with the mouse.
551 * \param c The client to resize.
552 * \param corner The corner to resize with.
553 * \param infobox Enable or disable the infobox.
556 mouse_client_resize_floating(client_t
*c
, corner_t corner
, bool infobox
)
558 xcb_screen_t
*screen
;
559 /* one corner of the client has a fixed position */
560 int fixed_x
, fixed_y
;
561 /* the other is moved with the mouse */
562 int mouse_x
= 0, mouse_y
= 0;
564 simple_window_t
*sw
= NULL
;
566 size_t cursor
= CurResize
;
567 int top
, bottom
, left
, right
;
569 screen
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
571 /* get current mouse position */
572 mouse_query_pointer(screen
->root
, &mouse_x
, &mouse_y
, NULL
);
575 bottom
= top
+ c
->geometry
.height
;
576 left
= c
->geometry
.x
;
577 right
= left
+ c
->geometry
.width
;
579 /* figure out which corner to move */
580 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
582 /* the opposite corner is fixed */
583 fixed_x
= (mouse_x
== left
) ? right
: left
;
584 fixed_y
= (mouse_y
== top
) ? bottom
: top
;
593 cursor
= CurTopRight
;
595 case BottomLeftCorner
:
598 case BottomRightCorner
:
599 cursor
= CurBotRight
;
603 /* grab the pointer */
604 if(!mouse_grab_pointer(screen
->root
, cursor
))
607 /* set pointer to the moveable corner */
608 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
610 /* create the infobox */
612 sw
= mouse_infobox_new(c
->phys_screen
, c
->border
, c
->geometry
, &ctx
);
614 /* for each motion event */
615 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
617 /* new client geometry */
618 area_t geo
= { .x
= MIN(fixed_x
, mouse_x
),
619 .y
= MIN(fixed_y
, mouse_y
),
620 .width
= (MAX(fixed_x
, mouse_x
) - MIN(fixed_x
, mouse_x
)),
621 .height
= (MAX(fixed_y
, mouse_y
) - MIN(fixed_y
, mouse_y
)) };
622 /* new moveable corner */
625 if(mouse_x
== AREA_LEFT(geo
))
626 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopLeftCorner
: BottomLeftCorner
;
628 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopRightCorner
: BottomRightCorner
;
631 if(corner
!= new_corner
)
637 default: cursor
= CurTopLeft
; break;
638 case TopRightCorner
: cursor
= CurTopRight
; break;
639 case BottomLeftCorner
: cursor
= CurBotLeft
; break;
640 case BottomRightCorner
: cursor
= CurBotRight
; break;
643 xcb_change_active_pointer_grab(globalconf
.connection
, globalconf
.cursor
[cursor
],
644 XCB_CURRENT_TIME
, MOUSEMASK
);
647 if(c
->hassizehints
&& c
->honorsizehints
)
651 /* apply size hints */
652 geo
= client_geometry_hints(c
, geo
);
654 /* get the nonmoveable corner back onto fixed_x,fixed_y */
657 default /* TopLeftCorner */:
658 dx
= fixed_x
- AREA_RIGHT(geo
);
659 dy
= fixed_y
- AREA_BOTTOM(geo
);
662 dx
= fixed_x
- AREA_LEFT(geo
);
663 dy
= fixed_y
- AREA_BOTTOM(geo
);
665 case BottomRightCorner
:
666 dx
= fixed_x
- AREA_LEFT(geo
);
667 dy
= fixed_y
- AREA_TOP(geo
);
669 case BottomLeftCorner
:
670 dx
= fixed_x
- AREA_RIGHT(geo
);
671 dy
= fixed_y
- AREA_TOP(geo
);
679 /* resize the client */
680 client_resize(c
, geo
, false);
681 /* redaw titlebar live */
684 /* draw the infobox */
686 mouse_infobox_draw(ctx
, sw
, c
->geometry
, c
->border
);
690 mouse_ungrab_pointer();
692 /* free the infobox */
695 draw_context_delete(&ctx
);
696 simplewindow_delete(&sw
);
700 /** Resize the master column/row of a tiled layout
701 * \param c A client on the tag/layout to resize.
704 mouse_client_resize_tiled(client_t
*c
)
706 xcb_screen_t
*screen
;
707 /* screen area modulo statusbar */
714 int mouse_x
= 0, mouse_y
= 0;
715 size_t cursor
= CurResize
;
717 screen
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
718 tag
= tags_get_current(c
->screen
)[0];
719 layout
= tag
->layout
;
721 area
= screen_area_get(tag
->screen
,
722 globalconf
.screens
[tag
->screen
].statusbar
,
723 &globalconf
.screens
[tag
->screen
].padding
);
725 mouse_query_pointer(screen
->root
, &mouse_x
, &mouse_y
, NULL
);
727 /* select initial pointer position */
728 if(layout
== layout_tile
)
730 mouse_x
= area
.x
+ area
.width
* tag
->mwfact
;
733 else if(layout
== layout_tileleft
)
735 mouse_x
= area
.x
+ area
.width
* (1. - tag
->mwfact
);
738 else if(layout
== layout_tilebottom
)
740 mouse_y
= area
.y
+ area
.height
* tag
->mwfact
;
743 else if(layout
== layout_tiletop
)
745 mouse_y
= area
.y
+ area
.height
* (1. - tag
->mwfact
);
751 /* grab the pointer */
752 if(!mouse_grab_pointer(screen
->root
, cursor
))
755 /* set pointer to the moveable border */
756 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
758 xcb_aux_sync(globalconf
.connection
);
760 /* for each motion event */
761 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
763 double mwfact
= 0, fact_x
, fact_y
;
765 /* calculate new master / rest ratio */
766 fact_x
= (double) (mouse_x
- area
.x
) / area
.width
;
767 fact_y
= (double) (mouse_y
- area
.y
) / area
.height
;
769 if(layout
== layout_tile
)
771 else if(layout
== layout_tileleft
)
772 mwfact
= 1. - fact_x
;
773 else if(layout
== layout_tilebottom
)
775 else if(layout
== layout_tiletop
)
776 mwfact
= 1. - fact_y
;
778 /* keep mwfact within reasonable bounds */
779 mwfact
= MIN(MAX( 0.01, mwfact
), 0.99);
782 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
784 tag
->mwfact
= mwfact
;
785 globalconf
.screens
[tag
->screen
].need_arrange
= true;
791 mouse_ungrab_pointer();
794 /** Resize the master client in mangifier layout
795 * \param c The client to resize.
796 * \param infobox Enable or disable the infobox.
799 mouse_client_resize_magnified(client_t
*c
, bool infobox
)
801 /* screen area modulo statusbar */
804 int center_x
, center_y
;
805 /* max. distance from the center */
808 int mouse_x
= 0, mouse_y
= 0;
809 /* cursor while grabbing */
810 size_t cursor
= CurResize
;
811 corner_t corner
= AutoCorner
;
815 simple_window_t
*sw
= NULL
;
819 tag
= tags_get_current(c
->screen
)[0];
821 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
823 area
= screen_area_get(tag
->screen
,
824 globalconf
.screens
[tag
->screen
].statusbar
,
825 &globalconf
.screens
[tag
->screen
].padding
);
827 center_x
= area
.x
+ (round(area
.width
/ 2.));
828 center_y
= area
.y
+ (round(area
.height
/ 2.));
830 maxdist
= round(sqrt((area
.width
*area
.width
) + (area
.height
*area
.height
)) / 2.);
832 /* get current mouse position */
833 mouse_query_pointer(root
, &mouse_x
, &mouse_y
, NULL
);
836 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
845 cursor
= CurTopRight
;
847 case BottomLeftCorner
:
850 case BottomRightCorner
:
851 cursor
= CurBotRight
;
856 if(!mouse_grab_pointer(root
, cursor
))
859 /* move pointer to corner */
860 mouse_warp_pointer(root
, mouse_x
, mouse_y
);
862 /* create the infobox */
864 sw
= mouse_infobox_new(c
->phys_screen
, c
->border
, c
->geometry
, &ctx
);
866 /* for each motion event */
867 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
869 /* \todo keep pointer on screen diagonals */
870 double mwfact
, dist
, dx
, dy
;
872 /* calc distance from the center */
873 dx
= center_x
- mouse_x
;
874 dy
= center_y
- mouse_y
;
875 dist
= sqrt((dx
*dx
) + (dy
*dy
));
877 /* new master/rest ratio */
878 mwfact
= dist
/ maxdist
;
880 /* keep mwfact within reasonable bounds */
881 mwfact
= MIN(MAX( 0.01, mwfact
), 0.99);
883 /* refresh the layout */
884 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
886 tag
->mwfact
= mwfact
;
887 globalconf
.screens
[tag
->screen
].need_arrange
= true;
891 /* draw the infobox */
893 mouse_infobox_draw(ctx
, sw
, c
->geometry
, c
->border
);
897 mouse_ungrab_pointer();
899 /* free the infobox */
902 draw_context_delete(&ctx
);
903 simplewindow_delete(&sw
);
907 /** Resize a client with the mouse.
908 * \param c The client to resize.
909 * \param corner The corner to use.
910 * \param infobox Enable or disable the info box.
913 mouse_client_resize(client_t
*c
, corner_t corner
, bool infobox
)
920 curtags
= tags_get_current(c
->screen
);
921 layout
= curtags
[0]->layout
;
922 s
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
924 /* only handle floating, tiled and magnifier layouts */
925 if(layout
== layout_floating
|| c
->isfloating
)
932 mouse_client_resize_floating(c
, corner
, infobox
);
934 else if(layout
== layout_tile
|| layout
== layout_tileleft
935 || layout
== layout_tilebottom
|| layout
== layout_tiletop
)
938 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
)
939 if(IS_TILED(c
, screen
))
942 /* only masters on this screen? */
943 if(n
<= curtags
[0]->nmaster
)
946 /* no tiled clients on this screen? */
947 for(c
= globalconf
.clients
; c
&& !IS_TILED(c
, screen
); c
= c
->next
);
951 mouse_client_resize_tiled(c
);
953 else if(layout
== layout_magnifier
)
954 mouse_client_resize_magnified(c
, infobox
);
960 /** Resize a client with mouse.
961 * \param L The Lua VM state.
965 * \lparam An optional table with keys: `corner', such as bottomleft,
966 * topright, etc, to specify which corner to grab (default to auto) and
967 * `infobox' to enable or disable the coordinates and dimensions box (default to
971 luaA_client_mouse_resize(lua_State
*L
)
973 client_t
**c
= luaA_checkudata(L
, 1, "client");
974 corner_t corner
= AutoCorner
;
979 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
981 luaA_checktable(L
, 2);
982 buf
= luaA_getopt_lstring(L
, 2, "corner", "auto", &len
);
983 corner
= a_strtocorner(buf
, len
);
984 infobox
= luaA_getopt_boolean(L
, 2, "infobox", true);
987 mouse_client_resize(*c
, corner
, infobox
);
992 /** Move a client with mouse.
993 * \param L The Lua VM state.
997 * \lparam An optional table with keys: `snap' for pixel to snap (default to 8), and
998 * `infobox' to enable or disable the coordinates and dimensions box (default to
1002 luaA_client_mouse_move(lua_State
*L
)
1004 client_t
**c
= luaA_checkudata(L
, 1, "client");
1006 bool infobox
= true;
1008 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
1010 luaA_checktable(L
, 2);
1011 snap
= luaA_getopt_number(L
, 2, "snap", 8);
1012 infobox
= luaA_getopt_boolean(L
, 2, "infobox", true);
1015 mouse_client_move(*c
, snap
, infobox
);
1020 /** Create a new mouse button bindings.
1021 * \param L The Lua VM state.
1022 * \return The number of elements pushed on stack.
1024 * \lparam A table with modifiers keys.
1025 * \lparam A mouse button number.
1026 * \lparam A function to execute on click events.
1027 * \lreturn A mouse button binding.
1030 luaA_mouse_new(lua_State
*L
)
1035 luaA_checktable(L
, 2);
1036 /* arg 3 is mouse button */
1037 i
= luaL_checknumber(L
, 3);
1038 /* arg 4 is cmd to run */
1039 luaA_checkfunction(L
, 4);
1041 button
= p_new(button_t
, 1);
1042 button
->button
= xutil_button_fromint(i
);
1043 button
->fct
= luaL_ref(L
, LUA_REGISTRYINDEX
);
1045 len
= lua_objlen(L
, 2);
1046 for(i
= 1; i
<= len
; i
++)
1048 lua_rawgeti(L
, 2, i
);
1049 button
->mod
|= xutil_keymask_fromstr(luaL_checkstring(L
, -1));
1052 return luaA_mouse_userdata_new(L
, button
);
1056 * \param L The Lua VM state.
1057 * \return The number of elements pushed on stack.
1059 * \lfield coords Mouse coordinates.
1060 * \lfield screen Mouse screen number.
1063 luaA_mouse_index(lua_State
*L
)
1066 const char *attr
= luaL_checklstring(L
, 2, &len
);
1067 int mouse_x
, mouse_y
, i
= 0;
1071 switch(a_tokenize(attr
, len
))
1074 root
= xutil_screen_get(globalconf
.connection
, globalconf
.default_screen
)->root
;
1076 if(!mouse_query_pointer(root
, &mouse_x
, &mouse_y
, &mask
))
1080 lua_pushnumber(L
, mouse_x
);
1081 lua_setfield(L
, -2, "x");
1082 lua_pushnumber(L
, mouse_y
);
1083 lua_setfield(L
, -2, "y");
1085 if (mask
& XCB_BUTTON_MASK_1
)
1087 lua_pushnumber(L
, 1);
1088 lua_rawseti(L
, -2, ++i
);
1090 if (mask
& XCB_BUTTON_MASK_2
)
1092 lua_pushnumber(L
, 2);
1093 lua_rawseti(L
, -2, ++i
);
1095 if (mask
& XCB_BUTTON_MASK_3
)
1097 lua_pushnumber(L
, 3);
1098 lua_rawseti(L
, -2, ++i
);
1100 if (mask
& XCB_BUTTON_MASK_4
)
1102 lua_pushnumber(L
, 4);
1103 lua_rawseti(L
, -2, ++i
);
1105 if (mask
& XCB_BUTTON_MASK_5
)
1107 lua_pushnumber(L
, 5);
1108 lua_rawseti(L
, -2, ++i
);
1110 lua_setfield(L
, -2, "buttons");
1113 root
= xutil_screen_get(globalconf
.connection
, globalconf
.default_screen
)->root
;
1115 if(!mouse_query_pointer(root
, &mouse_x
, &mouse_y
, NULL
))
1118 i
= screen_get_bycoord(globalconf
.screens_info
,
1119 globalconf
.default_screen
,
1122 lua_pushnumber(L
, i
+ 1);
1131 /** Newindex for mouse.
1132 * \param L The Lua VM state.
1133 * \return The number of elements pushed on stack.
1136 luaA_mouse_newindex(lua_State
*L
)
1139 const char *attr
= luaL_checklstring(L
, 2, &len
);
1140 int mouse_x
, mouse_y
, x
, y
= 0;
1144 switch(a_tokenize(attr
, len
))
1147 luaA_checktable(L
, 3);
1149 root
= xutil_screen_get(globalconf
.connection
, globalconf
.default_screen
)->root
;
1150 if(!mouse_query_pointer(root
, &mouse_x
, &mouse_y
, &mask
))
1153 x
= luaA_getopt_number(L
, 3, "x", mouse_x
);
1154 y
= luaA_getopt_number(L
, 3, "y", mouse_y
);
1156 mouse_warp_pointer(root
, x
, y
);
1165 const struct luaL_reg awesome_mouse_methods
[] =
1167 { "__call", luaA_mouse_new
},
1168 { "__index", luaA_mouse_index
},
1169 { "__newindex", luaA_mouse_newindex
},
1172 const struct luaL_reg awesome_mouse_meta
[] =
1174 { "__gc", luaA_mouse_gc
},
1175 { "__eq", luaA_mouse_eq
},
1179 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80