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 area_t screen_geometry_barless
=
138 screen_area_get(&globalconf
.screens
[c
->screen
].geometry
,
140 &globalconf
.screens
[c
->screen
].padding
);
142 geometry
= titlebar_geometry_add(c
->titlebar
, c
->border
, geometry
);
143 geometry
.width
+= 2 * c
->border
;
144 geometry
.height
+= 2 * c
->border
;
147 mouse_snapclienttogeometry_inside(geometry
, screen_geometry
, snap
);
150 mouse_snapclienttogeometry_inside(geometry
, screen_geometry_barless
, snap
);
152 for(snapper
= globalconf
.clients
; snapper
; snapper
= snapper
->next
)
153 if(snapper
!= c
&& client_isvisible(snapper
, c
->screen
))
155 snapper_geometry
= snapper
->geometry
;
156 snapper_geometry
.width
+= 2 * snapper
->border
;
157 snapper_geometry
.height
+= 2 * snapper
->border
;
158 snapper_geometry
= titlebar_geometry_add(snapper
->titlebar
, snapper
->border
, snapper_geometry
);
160 mouse_snapclienttogeometry_outside(geometry
,
165 geometry
.width
-= 2 * c
->border
;
166 geometry
.height
-= 2 * c
->border
;
167 return titlebar_geometry_remove(c
->titlebar
, c
->border
, geometry
);
170 /** Set coordinates to a corner of an area.
173 * \param[in,out] x The x coordinate.
174 * \param[in,out] y The y coordinate.
175 * \param corner The corner to snap to.
176 * \return The corner the coordinates have been set to. If corner != AutoCorner
177 * this is always equal to \em corner.
179 * \todo rename/move this is still awkward and might be useful somewhere else.
182 mouse_snap_to_corner(area_t a
, int *x
, int *y
, corner_t corner
)
184 int top
, bottom
, left
, right
;
187 bottom
= AREA_BOTTOM(a
);
189 right
= AREA_RIGHT(a
);
191 /* figure out the nearser corner */
192 if(corner
== AutoCorner
)
194 if(abs(top
- *y
) < abs(bottom
- *y
))
196 if(abs(left
- *x
) < abs(right
- *x
))
197 corner
= TopLeftCorner
;
199 corner
= TopRightCorner
;
203 if(abs(left
- *x
) < abs(right
- *x
))
204 corner
= BottomLeftCorner
;
206 corner
= BottomRightCorner
;
222 case BottomLeftCorner
:
227 case BottomRightCorner
:
239 /** Redraw the infobox.
240 * \param ctx Draw context.
241 * \param sw The simple window.
242 * \param geometry The geometry to use for the box.
243 * \param border The client border size.
246 mouse_infobox_draw(draw_context_t
*ctx
,
248 area_t geometry
, int border
)
250 area_t draw_geometry
= { 0, 0, ctx
->width
, ctx
->height
};
254 len
= snprintf(size
, sizeof(size
), "<text align=\"center\"/>%dx%d+%d+%d",
255 geometry
.width
, geometry
.height
, geometry
.x
, geometry
.y
);
256 draw_rectangle(ctx
, draw_geometry
, 1.0, true, &globalconf
.colors
.bg
);
257 draw_text(ctx
, globalconf
.font
, draw_geometry
, size
, len
, NULL
);
258 simplewindow_move(sw
,
259 geometry
.x
+ ((2 * border
+ geometry
.width
) - sw
->geometry
.width
) / 2,
260 geometry
.y
+ ((2 * border
+ geometry
.height
) - sw
->geometry
.height
) / 2);
261 simplewindow_refresh_pixmap(sw
);
262 xcb_aux_sync(ctx
->connection
);
265 #define MOUSE_INFOBOX_STRING_DEFAULT "0000x0000+0000+0000"
267 /** Initialize the infobox window.
268 * \param phys_screen Physical screen number.
269 * \param border Border size of the client.
270 * \param geometry Client geometry.
271 * \param ctx Draw context to create.
272 * \return The simple window.
274 static simple_window_t
*
275 mouse_infobox_new(int phys_screen
, int border
, area_t geometry
,
276 draw_context_t
**ctx
)
280 draw_parser_data_t pdata
;
282 draw_parser_data_init(&pdata
);
284 geom
= draw_text_extents(globalconf
.connection
,
285 globalconf
.default_screen
,
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;
293 sw
= simplewindow_new(globalconf
.connection
, phys_screen
,
295 geom
.width
, geom
.height
, 0);
297 *ctx
= draw_context_new(globalconf
.connection
, sw
->phys_screen
,
298 sw
->geometry
.width
, sw
->geometry
.height
,
300 &globalconf
.colors
.fg
,
301 &globalconf
.colors
.bg
);
303 xcb_map_window(globalconf
.connection
, sw
->window
);
304 mouse_infobox_draw(*ctx
, sw
, geometry
, border
);
306 draw_parser_data_wipe(&pdata
);
311 /** Get the pointer position.
312 * \param window The window to get position on.
313 * \param x will be set to the Pointer-x-coordinate relative to window
314 * \param y will be set to the Pointer-y-coordinate relative to window
315 * \param mask will be set to the current buttons state
316 * \return true on success, false if an error occured
319 mouse_query_pointer(xcb_window_t window
, int *x
, int *y
, uint16_t *mask
)
321 xcb_query_pointer_cookie_t query_ptr_c
;
322 xcb_query_pointer_reply_t
*query_ptr_r
;
324 query_ptr_c
= xcb_query_pointer_unchecked(globalconf
.connection
, window
);
325 query_ptr_r
= xcb_query_pointer_reply(globalconf
.connection
, query_ptr_c
, NULL
);
327 if(!query_ptr_r
|| !query_ptr_r
->same_screen
)
330 *x
= query_ptr_r
->win_x
;
331 *y
= query_ptr_r
->win_y
;
333 *mask
= query_ptr_r
->mask
;
335 p_delete(&query_ptr_r
);
340 /** Get the pointer position on the screen.
341 * \param screen This will be set to the screen number the mouse is on.
342 * \param x This will be set to the Pointer-x-coordinate relative to window.
343 * \param y This will be set to the Pointer-y-coordinate relative to window.
344 * \param mask This will be set to the current buttons state.
345 * \return True on success, false if an error occured.
348 mouse_query_pointer_root(int *s
, int *x
, int *y
, uint16_t *mask
)
351 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
354 xcb_window_t root
= xutil_screen_get(globalconf
.connection
, screen
)->root
;
356 if(mouse_query_pointer(root
, x
, y
, mask
))
365 /** Grab the Pointer.
366 * \param window The window grabbed.
367 * \param cursor The cursor to display (see struct.h CurNormal, CurResize, etc).
368 * \return True on success, false if an error occured.
371 mouse_grab_pointer(xcb_window_t window
, size_t cursor
)
373 xcb_grab_pointer_cookie_t grab_ptr_c
;
374 xcb_grab_pointer_reply_t
*grab_ptr_r
;
376 if(cursor
>= CurLast
)
379 grab_ptr_c
= xcb_grab_pointer_unchecked(globalconf
.connection
, false, window
,
380 MOUSEMASK
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
,
381 window
, globalconf
.cursor
[cursor
], XCB_CURRENT_TIME
);
382 grab_ptr_r
= xcb_grab_pointer_reply(globalconf
.connection
, grab_ptr_c
, NULL
);
387 p_delete(&grab_ptr_r
);
392 /** Ungrab the Pointer
395 mouse_ungrab_pointer(void)
397 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
400 /** Set the pointer position.
401 * \param window The destination window.
402 * \param x X-coordinate inside window.
403 * \param y Y-coordinate inside window.
406 mouse_warp_pointer(xcb_window_t window
, int x
, int y
)
408 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, window
,
412 /** Utility function to help with mouse-dragging
414 * \param x set to x-coordinate of the last event on return
415 * \param y set to y-coordinate of the last event on return
416 * \return true if an motion event was received
417 * false if an button release event was received
420 mouse_track_mouse_drag(int *x
, int *y
)
422 xcb_generic_event_t
*ev
;
423 xcb_motion_notify_event_t
*ev_motion
;
424 xcb_button_release_event_t
*ev_button
;
428 while((ev
= xcb_wait_for_event(globalconf
.connection
)))
430 switch((ev
->response_type
& 0x7F))
433 case XCB_MOTION_NOTIFY
:
434 ev_motion
= (xcb_motion_notify_event_t
*) ev
;
435 *x
= ev_motion
->event_x
;
436 *y
= ev_motion
->event_y
;
440 case XCB_BUTTON_RELEASE
:
441 ev_button
= (xcb_button_release_event_t
*) ev
;
442 *x
= ev_button
->event_x
;
443 *y
= ev_button
->event_y
;
448 xcb_handle_event(globalconf
.evenths
, ev
);
457 /** Get the client that contains the pointer.
459 * \return The client that contains the pointer or NULL.
462 mouse_get_client_under_pointer(int phys_screen
)
465 xcb_query_pointer_cookie_t query_ptr_c
;
466 xcb_query_pointer_reply_t
*query_ptr_r
;
469 root
= xutil_screen_get(globalconf
.connection
, phys_screen
)->root
;
471 query_ptr_c
= xcb_query_pointer_unchecked(globalconf
.connection
, root
);
472 query_ptr_r
= xcb_query_pointer_reply(globalconf
.connection
, query_ptr_c
, NULL
);
476 c
= client_getbywin(query_ptr_r
->child
);
477 p_delete(&query_ptr_r
);
483 /** Move the focused window with the mouse.
484 * \param c The client.
485 * \param snap The maximum distance in pixels to trigger a "snap".
486 * \param infobox Enable or disable the infobox.
489 mouse_client_move(client_t
*c
, int snap
, bool infobox
)
491 /* current mouse postion */
492 int mouse_x
, mouse_y
;
493 /* last mouse position */
494 int last_x
= 0, last_y
= 0;
498 simple_window_t
*sw
= NULL
;
500 /* the root window */
503 layout
= layout_get_current(c
->screen
);
504 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
506 /* get current pointer position */
507 mouse_query_pointer(root
, &last_x
, &last_y
, NULL
);
510 if(!mouse_grab_pointer(root
, CurMove
))
515 if(infobox
&& (c
->isfloating
|| layout
== layout_floating
))
516 sw
= mouse_infobox_new(c
->phys_screen
, c
->border
, c
->geometry
, &ctx
);
518 /* for each motion event */
519 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
520 if(c
->isfloating
|| layout
== layout_floating
)
524 /* calc new geometry */
525 geometry
= c
->geometry
;
526 geometry
.x
+= (mouse_x
- last_x
);
527 geometry
.y
+= (mouse_y
- last_y
);
530 geometry
= mouse_snapclient(c
, geometry
, snap
);
532 client_resize(c
, geometry
, false);
535 /* draw the infobox */
537 mouse_infobox_draw(ctx
, sw
, c
->geometry
, c
->border
);
548 /* client moved to another screen? */
549 newscreen
= screen_get_bycoord(globalconf
.screens_info
, c
->screen
,
551 if(newscreen
!= c
->screen
)
553 screen_client_moveto(c
, newscreen
, true);
554 globalconf
.screens
[c
->screen
].need_arrange
= true;
555 globalconf
.screens
[newscreen
].need_arrange
= true;
560 /* find client to swap with */
561 target
= mouse_get_client_under_pointer(c
->phys_screen
);
564 if(target
&& target
!= c
&& !target
->isfloating
)
566 client_list_swap(&globalconf
.clients
, c
, target
);
567 globalconf
.screens
[c
->screen
].need_arrange
= true;
574 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
576 /* free the infobox */
579 draw_context_delete(&ctx
);
580 simplewindow_delete(&sw
);
585 /** Resize a floating client with the mouse.
586 * \param c The client to resize.
587 * \param corner The corner to resize with.
588 * \param infobox Enable or disable the infobox.
591 mouse_client_resize_floating(client_t
*c
, corner_t corner
, bool infobox
)
593 xcb_screen_t
*screen
;
594 /* one corner of the client has a fixed position */
595 int fixed_x
, fixed_y
;
596 /* the other is moved with the mouse */
597 int mouse_x
= 0, mouse_y
= 0;
599 simple_window_t
*sw
= NULL
;
601 size_t cursor
= CurResize
;
602 int top
, bottom
, left
, right
;
604 screen
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
606 /* get current mouse position */
607 mouse_query_pointer(screen
->root
, &mouse_x
, &mouse_y
, NULL
);
610 bottom
= top
+ c
->geometry
.height
;
611 left
= c
->geometry
.x
;
612 right
= left
+ c
->geometry
.width
;
614 /* figure out which corner to move */
615 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
617 /* the opposite corner is fixed */
618 fixed_x
= (mouse_x
== left
) ? right
: left
;
619 fixed_y
= (mouse_y
== top
) ? bottom
: top
;
628 cursor
= CurTopRight
;
630 case BottomLeftCorner
:
633 case BottomRightCorner
:
634 cursor
= CurBotRight
;
638 /* grab the pointer */
639 if(!mouse_grab_pointer(screen
->root
, cursor
))
642 /* set pointer to the moveable corner */
643 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
645 /* create the infobox */
647 sw
= mouse_infobox_new(c
->phys_screen
, c
->border
, c
->geometry
, &ctx
);
649 /* for each motion event */
650 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
652 /* new client geometry */
653 area_t geo
= { .x
= MIN(fixed_x
, mouse_x
),
654 .y
= MIN(fixed_y
, mouse_y
),
655 .width
= (MAX(fixed_x
, mouse_x
) - MIN(fixed_x
, mouse_x
)),
656 .height
= (MAX(fixed_y
, mouse_y
) - MIN(fixed_y
, mouse_y
)) };
657 /* new moveable corner */
660 if(mouse_x
== AREA_LEFT(geo
))
661 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopLeftCorner
: BottomLeftCorner
;
663 new_corner
= (mouse_y
== AREA_TOP(geo
)) ? TopRightCorner
: BottomRightCorner
;
666 if(corner
!= new_corner
)
672 default: cursor
= CurTopLeft
; break;
673 case TopRightCorner
: cursor
= CurTopRight
; break;
674 case BottomLeftCorner
: cursor
= CurBotLeft
; break;
675 case BottomRightCorner
: cursor
= CurBotRight
; break;
678 xcb_change_active_pointer_grab(globalconf
.connection
, globalconf
.cursor
[cursor
],
679 XCB_CURRENT_TIME
, MOUSEMASK
);
682 if(c
->hassizehints
&& c
->honorsizehints
)
686 /* apply size hints */
687 geo
= client_geometry_hints(c
, geo
);
689 /* get the nonmoveable corner back onto fixed_x,fixed_y */
692 default /* TopLeftCorner */:
693 dx
= fixed_x
- AREA_RIGHT(geo
);
694 dy
= fixed_y
- AREA_BOTTOM(geo
);
697 dx
= fixed_x
- AREA_LEFT(geo
);
698 dy
= fixed_y
- AREA_BOTTOM(geo
);
700 case BottomRightCorner
:
701 dx
= fixed_x
- AREA_LEFT(geo
);
702 dy
= fixed_y
- AREA_TOP(geo
);
704 case BottomLeftCorner
:
705 dx
= fixed_x
- AREA_RIGHT(geo
);
706 dy
= fixed_y
- AREA_TOP(geo
);
714 /* resize the client */
715 client_resize(c
, geo
, false);
716 /* redaw titlebar live */
719 /* draw the infobox */
721 mouse_infobox_draw(ctx
, sw
, c
->geometry
, c
->border
);
725 mouse_ungrab_pointer();
727 /* free the infobox */
730 draw_context_delete(&ctx
);
731 simplewindow_delete(&sw
);
735 /** Resize the master column/row of a tiled layout
736 * \param c A client on the tag/layout to resize.
739 mouse_client_resize_tiled(client_t
*c
)
741 xcb_screen_t
*screen
;
742 /* screen area modulo statusbar */
749 int mouse_x
= 0, mouse_y
= 0;
750 size_t cursor
= CurResize
;
752 screen
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
753 tag
= tags_get_current(c
->screen
)[0];
754 layout
= tag
->layout
;
756 area
= screen_area_get(&globalconf
.screens
[tag
->screen
].geometry
,
757 globalconf
.screens
[tag
->screen
].statusbar
,
758 &globalconf
.screens
[tag
->screen
].padding
);
760 mouse_query_pointer(screen
->root
, &mouse_x
, &mouse_y
, NULL
);
762 /* select initial pointer position */
763 if(layout
== layout_tile
)
765 mouse_x
= area
.x
+ area
.width
* tag
->mwfact
;
768 else if(layout
== layout_tileleft
)
770 mouse_x
= area
.x
+ area
.width
* (1. - tag
->mwfact
);
773 else if(layout
== layout_tilebottom
)
775 mouse_y
= area
.y
+ area
.height
* tag
->mwfact
;
778 else if(layout
== layout_tiletop
)
780 mouse_y
= area
.y
+ area
.height
* (1. - tag
->mwfact
);
786 /* grab the pointer */
787 if(!mouse_grab_pointer(screen
->root
, cursor
))
790 /* set pointer to the moveable border */
791 mouse_warp_pointer(screen
->root
, mouse_x
, mouse_y
);
793 xcb_aux_sync(globalconf
.connection
);
795 /* for each motion event */
796 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
798 double mwfact
= 0, fact_x
, fact_y
;
800 /* calculate new master / rest ratio */
801 fact_x
= (double) (mouse_x
- area
.x
) / area
.width
;
802 fact_y
= (double) (mouse_y
- area
.y
) / area
.height
;
804 if(layout
== layout_tile
)
806 else if(layout
== layout_tileleft
)
807 mwfact
= 1. - fact_x
;
808 else if(layout
== layout_tilebottom
)
810 else if(layout
== layout_tiletop
)
811 mwfact
= 1. - fact_y
;
813 /* keep mwfact within reasonable bounds */
814 mwfact
= MIN(MAX( 0.01, mwfact
), 0.99);
817 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
819 tag
->mwfact
= mwfact
;
820 globalconf
.screens
[tag
->screen
].need_arrange
= true;
827 mouse_ungrab_pointer();
830 /** Resize the master client in mangifier layout
831 * \param c The client to resize.
832 * \param infobox Enable or disable the infobox.
835 mouse_client_resize_magnified(client_t
*c
, bool infobox
)
837 /* screen area modulo statusbar */
840 int center_x
, center_y
;
841 /* max. distance from the center */
844 int mouse_x
= 0, mouse_y
= 0;
845 /* cursor while grabbing */
846 size_t cursor
= CurResize
;
847 corner_t corner
= AutoCorner
;
851 simple_window_t
*sw
= NULL
;
855 tag
= tags_get_current(c
->screen
)[0];
857 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
859 area
= screen_area_get(&globalconf
.screens
[tag
->screen
].geometry
,
860 globalconf
.screens
[tag
->screen
].statusbar
,
861 &globalconf
.screens
[tag
->screen
].padding
);
863 center_x
= area
.x
+ (round(area
.width
/ 2.));
864 center_y
= area
.y
+ (round(area
.height
/ 2.));
866 maxdist
= round(sqrt((area
.width
*area
.width
) + (area
.height
*area
.height
)) / 2.);
868 root
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
)->root
;
870 if(!mouse_query_pointer(root
, &mouse_x
, &mouse_y
, NULL
))
874 corner
= mouse_snap_to_corner(c
->geometry
, &mouse_x
, &mouse_y
, corner
);
883 cursor
= CurTopRight
;
885 case BottomLeftCorner
:
888 case BottomRightCorner
:
889 cursor
= CurBotRight
;
894 if(!mouse_grab_pointer(root
, cursor
))
897 /* move pointer to corner */
898 mouse_warp_pointer(root
, mouse_x
, mouse_y
);
900 /* create the infobox */
902 sw
= mouse_infobox_new(c
->phys_screen
, c
->border
, c
->geometry
, &ctx
);
904 /* for each motion event */
905 while(mouse_track_mouse_drag(&mouse_x
, &mouse_y
))
907 /* \todo keep pointer on screen diagonals */
908 double mwfact
, dist
, dx
, dy
;
910 /* calc distance from the center */
911 dx
= center_x
- mouse_x
;
912 dy
= center_y
- mouse_y
;
913 dist
= sqrt((dx
*dx
) + (dy
*dy
));
915 /* new master/rest ratio */
916 mwfact
= dist
/ maxdist
;
918 /* keep mwfact within reasonable bounds */
919 mwfact
= MIN(MAX( 0.01, mwfact
), 0.99);
921 /* refresh the layout */
922 if(fabs(tag
->mwfact
- mwfact
) >= 0.01)
924 tag
->mwfact
= mwfact
;
925 globalconf
.screens
[tag
->screen
].need_arrange
= true;
930 /* draw the infobox */
932 mouse_infobox_draw(ctx
, sw
, c
->geometry
, c
->border
);
936 mouse_ungrab_pointer();
938 /* free the infobox */
941 draw_context_delete(&ctx
);
942 simplewindow_delete(&sw
);
946 /** Resize a client with the mouse.
947 * \param c The client to resize.
948 * \param corner The corner to use.
949 * \param infobox Enable or disable the info box.
952 mouse_client_resize(client_t
*c
, corner_t corner
, bool infobox
)
959 curtags
= tags_get_current(c
->screen
);
960 layout
= curtags
[0]->layout
;
961 s
= xutil_screen_get(globalconf
.connection
, c
->phys_screen
);
963 /* only handle floating, tiled and magnifier layouts */
964 if(layout
== layout_floating
|| c
->isfloating
)
971 mouse_client_resize_floating(c
, corner
, infobox
);
973 else if(layout
== layout_tile
|| layout
== layout_tileleft
974 || layout
== layout_tilebottom
|| layout
== layout_tiletop
)
977 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
)
978 if(IS_TILED(c
, screen
))
981 /* only masters on this screen? */
982 if(n
<= curtags
[0]->nmaster
)
985 /* no tiled clients on this screen? */
986 for(c
= globalconf
.clients
; c
&& !IS_TILED(c
, screen
); c
= c
->next
);
990 mouse_client_resize_tiled(c
);
992 else if(layout
== layout_magnifier
)
993 mouse_client_resize_magnified(c
, infobox
);
999 /** Resize a client with mouse.
1000 * \param L The Lua VM state.
1004 * \lparam An optional table with keys: `corner', such as bottomleft,
1005 * topright, etc, to specify which corner to grab (default to auto) and
1006 * `infobox' to enable or disable the coordinates and dimensions box (default to
1010 luaA_client_mouse_resize(lua_State
*L
)
1012 client_t
**c
= luaA_checkudata(L
, 1, "client");
1013 corner_t corner
= AutoCorner
;
1014 bool infobox
= true;
1018 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
1020 luaA_checktable(L
, 2);
1021 buf
= luaA_getopt_lstring(L
, 2, "corner", "auto", &len
);
1022 corner
= a_strtocorner(buf
, len
);
1023 infobox
= luaA_getopt_boolean(L
, 2, "infobox", true);
1026 mouse_client_resize(*c
, corner
, infobox
);
1031 /** Move a client with mouse.
1032 * \param L The Lua VM state.
1036 * \lparam An optional table with keys: `snap' for pixel to snap (default to 8), and
1037 * `infobox' to enable or disable the coordinates and dimensions box (default to
1041 luaA_client_mouse_move(lua_State
*L
)
1043 client_t
**c
= luaA_checkudata(L
, 1, "client");
1045 bool infobox
= true;
1047 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
1049 luaA_checktable(L
, 2);
1050 snap
= luaA_getopt_number(L
, 2, "snap", 8);
1051 infobox
= luaA_getopt_boolean(L
, 2, "infobox", true);
1054 mouse_client_move(*c
, snap
, infobox
);
1059 /** Create a new mouse button bindings.
1060 * \param L The Lua VM state.
1061 * \return The number of elements pushed on stack.
1063 * \lparam A table with modifiers keys.
1064 * \lparam A mouse button number.
1065 * \lparam A function to execute on click events.
1066 * \lreturn A mouse button binding.
1069 luaA_mouse_new(lua_State
*L
)
1074 luaA_checktable(L
, 2);
1075 /* arg 3 is mouse button */
1076 i
= luaL_checknumber(L
, 3);
1077 /* arg 4 is cmd to run */
1078 luaA_checkfunction(L
, 4);
1080 button
= p_new(button_t
, 1);
1081 button
->button
= xutil_button_fromint(i
);
1082 button
->fct
= luaL_ref(L
, LUA_REGISTRYINDEX
);
1084 len
= lua_objlen(L
, 2);
1085 for(i
= 1; i
<= len
; i
++)
1087 lua_rawgeti(L
, 2, i
);
1088 button
->mod
|= xutil_key_mask_fromstr(luaL_checkstring(L
, -1));
1091 return luaA_mouse_userdata_new(L
, button
);
1095 * \param L The Lua VM state.
1096 * \return The number of elements pushed on stack.
1098 * \lfield coords Mouse coordinates.
1099 * \lfield screen Mouse screen number.
1102 luaA_mouse_index(lua_State
*L
)
1105 const char *attr
= luaL_checklstring(L
, 2, &len
);
1106 int mouse_x
, mouse_y
, i
= 0;
1110 switch(a_tokenize(attr
, len
))
1113 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, &mask
))
1117 lua_pushnumber(L
, mouse_x
);
1118 lua_setfield(L
, -2, "x");
1119 lua_pushnumber(L
, mouse_y
);
1120 lua_setfield(L
, -2, "y");
1122 if (mask
& XCB_BUTTON_MASK_1
)
1124 lua_pushnumber(L
, 1);
1125 lua_rawseti(L
, -2, ++i
);
1127 if (mask
& XCB_BUTTON_MASK_2
)
1129 lua_pushnumber(L
, 2);
1130 lua_rawseti(L
, -2, ++i
);
1132 if (mask
& XCB_BUTTON_MASK_3
)
1134 lua_pushnumber(L
, 3);
1135 lua_rawseti(L
, -2, ++i
);
1137 if (mask
& XCB_BUTTON_MASK_4
)
1139 lua_pushnumber(L
, 4);
1140 lua_rawseti(L
, -2, ++i
);
1142 if (mask
& XCB_BUTTON_MASK_5
)
1144 lua_pushnumber(L
, 5);
1145 lua_rawseti(L
, -2, ++i
);
1147 lua_setfield(L
, -2, "buttons");
1150 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, NULL
))
1153 i
= screen_get_bycoord(globalconf
.screens_info
, screen
, mouse_x
, mouse_y
);
1155 lua_pushnumber(L
, i
+ 1);
1164 /** Newindex for mouse.
1165 * \param L The Lua VM state.
1166 * \return The number of elements pushed on stack.
1169 luaA_mouse_newindex(lua_State
*L
)
1172 const char *attr
= luaL_checklstring(L
, 2, &len
);
1173 int mouse_x
, mouse_y
, x
, y
= 0;
1176 int screen
, phys_screen
;
1178 switch(a_tokenize(attr
, len
))
1181 luaA_checktable(L
, 3);
1183 if(!mouse_query_pointer_root(&screen
, &mouse_x
, &mouse_y
, &mask
))
1186 x
= luaA_getopt_number(L
, 3, "x", mouse_x
);
1187 y
= luaA_getopt_number(L
, 3, "y", mouse_y
);
1189 root
= xutil_screen_get(globalconf
.connection
, screen
)->root
;
1190 mouse_warp_pointer(root
, x
, y
);
1193 screen
= luaL_checknumber(L
, 3) - 1;
1194 luaA_checkscreen(screen
);
1196 /* we need the physical one to get the root window */
1197 phys_screen
= screen_virttophys(screen
);
1198 root
= xutil_screen_get(globalconf
.connection
, phys_screen
)->root
;
1200 x
= globalconf
.screens_info
->geometry
[screen
].x
;
1201 y
= globalconf
.screens_info
->geometry
[screen
].y
;
1203 mouse_warp_pointer(root
, x
, y
);
1212 const struct luaL_reg awesome_mouse_methods
[] =
1214 { "__call", luaA_mouse_new
},
1215 { "__index", luaA_mouse_index
},
1216 { "__newindex", luaA_mouse_newindex
},
1219 const struct luaL_reg awesome_mouse_meta
[] =
1221 { "__gc", luaA_mouse_gc
},
1222 { "__eq", luaA_mouse_eq
},
1226 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80