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.
25 #include <xcb/xcb_aux.h>
33 #include "layouts/floating.h"
34 #include "layouts/tile.h"
36 #define MOUSEMASK (XCB_EVENT_MASK_BUTTON_PRESS \
37 | XCB_EVENT_MASK_BUTTON_RELEASE \
38 | XCB_EVENT_MASK_POINTER_MOTION)
40 extern AwesomeConf globalconf
;
42 /** Snap an area to the outside of an area.
43 * \param geometry geometry of the area to snap
44 * \param snap_geometry geometry of snapping area
45 * \param snap snap trigger in pixel
46 * \return snapped geometry
49 mouse_snapclienttogeometry_outside(area_t geometry
, area_t snap_geometry
, int snap
)
51 if(geometry
.x
< snap
+ snap_geometry
.x
+ snap_geometry
.width
52 && geometry
.x
> snap_geometry
.x
+ snap_geometry
.width
)
53 geometry
.x
= snap_geometry
.x
+ snap_geometry
.width
;
54 else if(geometry
.x
+ geometry
.width
< snap_geometry
.x
55 && geometry
.x
+ geometry
.width
> snap_geometry
.x
- snap
)
56 geometry
.x
= snap_geometry
.x
- geometry
.width
;
58 if(geometry
.y
< snap
+ snap_geometry
.y
+ snap_geometry
.height
59 && geometry
.y
> snap_geometry
.y
+ snap_geometry
.height
)
60 geometry
.y
= snap_geometry
.y
+ snap_geometry
.height
;
61 else if(geometry
.y
+ geometry
.height
< snap_geometry
.y
62 && geometry
.y
+ geometry
.height
> snap_geometry
.y
- snap
)
63 geometry
.y
= snap_geometry
.y
- geometry
.height
;
68 /** Snap an area to the inside of an area.
69 * \param geometry geometry of the area to snap
70 * \param snap_geometry geometry of snapping area
71 * \param snap snap trigger in pixel
72 * \return snapped geometry
75 mouse_snapclienttogeometry_inside(area_t geometry
, area_t snap_geometry
, int snap
)
77 if(abs(geometry
.x
) < snap
+ snap_geometry
.x
&& geometry
.x
> snap_geometry
.x
)
78 geometry
.x
= snap_geometry
.x
;
79 else if(abs((snap_geometry
.x
+ snap_geometry
.width
) - (geometry
.x
+ geometry
.width
))
81 geometry
.x
= snap_geometry
.x
+ snap_geometry
.width
- geometry
.width
;
82 if(abs(geometry
.y
) < snap
+ snap_geometry
.y
&& geometry
.y
> snap_geometry
.y
)
83 geometry
.y
= snap_geometry
.y
;
84 else if(abs((snap_geometry
.y
+ snap_geometry
.height
) - (geometry
.y
+ geometry
.height
))
86 geometry
.y
= snap_geometry
.y
+ snap_geometry
.height
- geometry
.height
;
91 /** Snap a client with a futur geometry to the screen and other clients.
93 * \param geometry geometry the client will get
94 * \return geometry to set to the client
97 mouse_snapclient(client_t
*c
, area_t geometry
)
100 int snap
= globalconf
.screens
[c
->screen
].snap
;
101 area_t snapper_geometry
;
102 area_t screen_geometry
=
103 screen_get_area(c
->screen
,
104 globalconf
.screens
[c
->screen
].statusbar
,
105 &globalconf
.screens
[c
->screen
].padding
);
107 geometry
= titlebar_geometry_add(&c
->titlebar
, geometry
);
108 geometry
.width
+= 2 * c
->border
;
109 geometry
.height
+= 2 * c
->border
;
112 mouse_snapclienttogeometry_inside(geometry
, screen_geometry
, snap
);
114 for(snapper
= globalconf
.clients
; snapper
; snapper
= snapper
->next
)
115 if(snapper
!= c
&& client_isvisible(c
, c
->screen
))
117 snapper_geometry
= snapper
->geometry
;
118 snapper_geometry
.width
+= 2 * c
->border
;
119 snapper_geometry
.height
+= 2 * c
->border
;
120 snapper_geometry
= titlebar_geometry_add(&snapper
->titlebar
,
123 mouse_snapclienttogeometry_outside(geometry
,
128 geometry
.width
-= 2 * c
->border
;
129 geometry
.height
-= 2 * c
->border
;
130 return titlebar_geometry_remove(&c
->titlebar
, geometry
);
133 /** Redraw the resizebar.
134 * \param ctx Draw context.
135 * \param style The style pointer to use for drawing.
136 * \param geometry The geometry to use for the box.
137 * \param border The client border size.
140 mouse_resizebar_draw(draw_context_t
*ctx
, style_t
*style
,
141 simple_window_t
*sw
, area_t geometry
, int border
)
143 area_t draw_geometry
= { 0, 0, ctx
->width
, ctx
->height
, NULL
, NULL
};
146 snprintf(size
, sizeof(size
), "<text align=\"center\"/>%dx%d+%d+%d",
147 geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
148 draw_text(ctx
, draw_geometry
, size
, style
);
149 simplewindow_move(sw
,
150 geometry
.x
+ ((2 * border
+ geometry
.width
) - sw
->geometry
.width
) / 2,
151 geometry
.y
+ ((2 * border
+ geometry
.height
) - sw
->geometry
.height
) / 2);
152 simplewindow_refresh_drawable(sw
);
155 /** Initialize the resizebar window.
156 * \param phys_screen Physical screen number.
157 * \param border Border size of the client.
158 * \param geometry Client geometry.
159 * \param style Style used to draw.
160 * \param ctx Draw context to create.
161 * \return The simple window.
163 static simple_window_t
*
164 mouse_resizebar_new(int phys_screen
, int border
, area_t geometry
,
165 style_t
*style
, draw_context_t
**ctx
)
170 geom
= draw_text_extents(globalconf
.connection
,
171 globalconf
.default_screen
,
173 "0000x0000+0000+0000");
174 geom
.x
= geometry
.x
+ ((2 * border
+ geometry
.width
) - geom
.width
) / 2;
175 geom
.y
= geometry
.y
+ ((2 * border
+ geometry
.height
) - geom
.height
) / 2;
177 sw
= simplewindow_new(globalconf
.connection
, phys_screen
,
179 geom
.width
, geom
.height
, 0);
181 *ctx
= draw_context_new(globalconf
.connection
, sw
->phys_screen
,
182 sw
->geometry
.width
, sw
->geometry
.height
,
185 xcb_map_window(globalconf
.connection
, sw
->window
);
186 mouse_resizebar_draw(*ctx
, style
, sw
, geometry
, border
);
191 /** Move the focused window with the mouse.
192 * \param screen Screen ID
194 * \ingroup ui_callback
197 uicb_client_movemouse(int screen
, char *arg
__attribute__ ((unused
)))
199 int ocx
, ocy
, newscreen
;
201 client_t
*c
= globalconf
.focus
->client
, *target
;
202 Layout
*layout
= layout_get_current(screen
);
203 simple_window_t
*sw
= NULL
;
206 xcb_generic_event_t
*ev
= NULL
;
207 xcb_motion_notify_event_t
*ev_motion
= NULL
;
208 xcb_grab_pointer_reply_t
*grab_pointer_r
= NULL
;
209 xcb_grab_pointer_cookie_t grab_pointer_c
;
210 xcb_query_pointer_reply_t
*query_pointer_r
= NULL
, *mquery_pointer_r
= NULL
;
211 xcb_query_pointer_cookie_t query_pointer_c
;
212 xcb_screen_t
*s
= xcb_aux_get_screen(globalconf
.connection
, c
->phys_screen
);
217 /* Send pointer requests */
218 grab_pointer_c
= xcb_grab_pointer(globalconf
.connection
, false, s
->root
,
219 MOUSEMASK
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
,
220 s
->root
, globalconf
.cursor
[CurMove
], XCB_CURRENT_TIME
);
222 query_pointer_c
= xcb_query_pointer_unchecked(globalconf
.connection
, s
->root
);
224 geometry
= c
->geometry
;
227 style
= &globalconf
.screens
[c
->screen
].styles
.focus
;
230 if(!(grab_pointer_r
= xcb_grab_pointer_reply(globalconf
.connection
, grab_pointer_c
, NULL
)))
235 p_delete(&grab_pointer_r
);
237 query_pointer_r
= xcb_query_pointer_reply(globalconf
.connection
, query_pointer_c
, NULL
);
239 if(c
->isfloating
|| layout
->arrange
== layout_floating
)
241 sw
= mouse_resizebar_new(c
->phys_screen
, c
->border
, c
->geometry
, style
, &ctx
);
242 xcb_aux_sync(globalconf
.connection
);
247 /* XMaskEvent allows to retrieve only specified events from
248 * the queue and requeue the other events... */
249 while(ev
|| (ev
= xcb_wait_for_event(globalconf
.connection
)))
251 switch((ev
->response_type
& 0x7f))
253 case XCB_BUTTON_RELEASE
:
254 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
257 draw_context_delete(&ctx
);
258 simplewindow_delete(&sw
);
260 p_delete(&query_pointer_r
);
263 case XCB_MOTION_NOTIFY
:
264 if(c
->isfloating
|| layout
->arrange
== layout_floating
)
266 ev_motion
= (xcb_motion_notify_event_t
*) ev
;
268 geometry
.x
= ocx
+ (ev_motion
->event_x
- query_pointer_r
->root_x
);
269 geometry
.y
= ocy
+ (ev_motion
->event_y
- query_pointer_r
->root_y
);
271 geometry
= mouse_snapclient(c
, geometry
);
273 client_resize(c
, geometry
, false);
277 mouse_resizebar_draw(ctx
, style
, sw
, c
->geometry
, c
->border
);
279 xcb_aux_sync(globalconf
.connection
);
283 query_pointer_c
= xcb_query_pointer_unchecked(globalconf
.connection
, s
->root
);
284 mquery_pointer_r
= xcb_query_pointer_reply(globalconf
.connection
, query_pointer_c
, NULL
);
285 if((newscreen
= screen_get_bycoord(globalconf
.screens_info
, c
->screen
,
286 mquery_pointer_r
->root_x
,
287 mquery_pointer_r
->root_y
)) != c
->screen
)
289 move_client_to_screen(c
, newscreen
, true);
290 globalconf
.screens
[c
->screen
].need_arrange
= true;
291 globalconf
.screens
[newscreen
].need_arrange
= true;
294 if((target
= client_get_bywin(globalconf
.clients
, mquery_pointer_r
->child
))
295 && target
!= c
&& !target
->isfloating
)
297 client_list_swap(&globalconf
.clients
, c
, target
);
298 globalconf
.screens
[c
->screen
].need_arrange
= true;
301 p_delete(&mquery_pointer_r
);
304 while((ev
= xcb_poll_for_event(globalconf
.connection
))
305 && (ev
->response_type
& 0x7f) == XCB_MOTION_NOTIFY
)
309 xcb_handle_event(globalconf
.evenths
, ev
);
317 /** Resize the focused window with the mouse.
318 * \param screen Screen ID
320 * \ingroup ui_callback
323 uicb_client_resizemouse(int screen
, char *arg
__attribute__ ((unused
)))
325 int ocx
= 0, ocy
= 0, n
;
326 xcb_generic_event_t
*ev
= NULL
;
327 xcb_motion_notify_event_t
*ev_motion
= NULL
;
328 client_t
*c
= globalconf
.focus
->client
;
329 tag_t
**curtags
= tags_get_current(screen
);
330 Layout
*layout
= curtags
[0]->layout
;
331 area_t area
= { 0, 0, 0, 0, NULL
, NULL
}, geometry
= { 0, 0, 0, 0, NULL
, NULL
};
333 simple_window_t
*sw
= NULL
;
334 draw_context_t
*ctx
= NULL
;
336 xcb_grab_pointer_cookie_t grab_pointer_c
;
337 xcb_grab_pointer_reply_t
*grab_pointer_r
= NULL
;
338 xcb_screen_t
*s
= xcb_aux_get_screen(globalconf
.connection
, c
->phys_screen
);
340 /* only handle floating and tiled layouts */
344 style
= &globalconf
.screens
[c
->screen
].styles
.focus
;
346 if(layout
->arrange
== layout_floating
|| c
->isfloating
)
352 sw
= mouse_resizebar_new(c
->phys_screen
, c
->border
, c
->geometry
, style
, &ctx
);
354 else if (layout
->arrange
== layout_tile
|| layout
->arrange
== layout_tileleft
355 || layout
->arrange
== layout_tilebottom
|| layout
->arrange
== layout_tiletop
)
357 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
)
358 if(IS_TILED(c
, screen
))
361 if(n
<= curtags
[0]->nmaster
) return;
363 for(c
= globalconf
.clients
; c
&& !IS_TILED(c
, screen
); c
= c
->next
);
366 area
= screen_get_area(screen
,
367 globalconf
.screens
[c
->screen
].statusbar
,
368 &globalconf
.screens
[c
->screen
].padding
);
373 grab_pointer_c
= xcb_grab_pointer(globalconf
.connection
, false, s
->root
,
374 MOUSEMASK
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
,
375 s
->root
, globalconf
.cursor
[CurResize
], XCB_CURRENT_TIME
);
377 if(!(grab_pointer_r
= xcb_grab_pointer_reply(globalconf
.connection
,
378 grab_pointer_c
, NULL
)))
381 p_delete(&grab_pointer_r
);
383 if(curtags
[0]->layout
->arrange
== layout_tileleft
)
384 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, c
->win
, 0, 0, 0, 0,
385 0, c
->geometry
.height
+ c
->border
- 1);
386 else if(curtags
[0]->layout
->arrange
== layout_tiletop
)
387 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, c
->win
, 0, 0, 0, 0,
388 c
->geometry
.width
+ c
->border
- 1, 0);
390 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, c
->win
, 0, 0, 0, 0,
391 c
->geometry
.width
+ c
->border
- 1,
392 c
->geometry
.height
+ c
->border
- 1);
394 xcb_aux_sync(globalconf
.connection
);
398 /* XMaskEvent allows to retrieve only specified events from
399 * the queue and requeue the other events... */
400 while(ev
|| (ev
= xcb_wait_for_event(globalconf
.connection
)))
402 switch((ev
->response_type
& 0x7f))
404 case XCB_BUTTON_RELEASE
:
407 draw_context_delete(&ctx
);
408 simplewindow_delete(&sw
);
410 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
414 case XCB_MOTION_NOTIFY
:
415 ev_motion
= (xcb_motion_notify_event_t
*) ev
;
417 if(layout
->arrange
== layout_floating
|| c
->isfloating
)
419 if((geometry
.width
= ev_motion
->event_x
- ocx
- 2 * c
->border
+ 1) <= 0)
421 if((geometry
.height
= ev_motion
->event_y
- ocy
- 2 * c
->border
+ 1) <= 0)
423 geometry
.x
= c
->geometry
.x
;
424 geometry
.y
= c
->geometry
.y
;
425 client_resize(c
, geometry
, true);
427 mouse_resizebar_draw(ctx
, style
, sw
, c
->geometry
, c
->border
);
428 xcb_aux_sync(globalconf
.connection
);
430 else if(layout
->arrange
== layout_tile
|| layout
->arrange
== layout_tileleft
431 || layout
->arrange
== layout_tiletop
|| layout
->arrange
== layout_tilebottom
)
433 if(layout
->arrange
== layout_tile
)
434 mwfact
= (double) (ev_motion
->event_x
- area
.x
) / area
.width
;
435 else if(curtags
[0]->layout
->arrange
== layout_tileleft
)
436 mwfact
= 1 - (double) (ev_motion
->event_x
- area
.x
) / area
.width
;
437 else if(curtags
[0]->layout
->arrange
== layout_tilebottom
)
438 mwfact
= (double) (ev_motion
->event_y
- area
.y
) / area
.height
;
440 mwfact
= 1 - (double) (ev_motion
->event_y
- area
.y
) / area
.height
;
441 mwfact
= MAX(globalconf
.screens
[screen
].mwfact_lower_limit
,
442 MIN(globalconf
.screens
[screen
].mwfact_upper_limit
, mwfact
));
443 if(fabs(curtags
[0]->mwfact
- mwfact
) >= 0.01)
445 curtags
[0]->mwfact
= mwfact
;
446 globalconf
.screens
[screen
].need_arrange
= true;
451 while((ev
= xcb_poll_for_event(globalconf
.connection
))
452 && (ev
->response_type
& 0x7f) == XCB_MOTION_NOTIFY
)
456 xcb_handle_event(globalconf
.evenths
, ev
);
464 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80