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"
35 #include "common/xscreen.h"
37 #define MOUSEMASK (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION)
39 extern AwesomeConf globalconf
;
42 mouse_snapclienttogeometry_outside(area_t geometry
, area_t snap_geometry
, int snap
)
44 if(geometry
.x
< snap
+ snap_geometry
.x
+ snap_geometry
.width
45 && geometry
.x
> snap_geometry
.x
+ snap_geometry
.width
)
46 geometry
.x
= snap_geometry
.x
+ snap_geometry
.width
;
47 else if(geometry
.x
+ geometry
.width
< snap_geometry
.x
48 && geometry
.x
+ geometry
.width
> snap_geometry
.x
- snap
)
49 geometry
.x
= snap_geometry
.x
- geometry
.width
;
52 if(geometry
.y
< snap
+ snap_geometry
.y
+ snap_geometry
.height
53 && geometry
.y
> snap_geometry
.y
+ snap_geometry
.height
)
54 geometry
.y
= snap_geometry
.y
+ snap_geometry
.height
;
55 else if(geometry
.y
+ geometry
.height
< snap_geometry
.y
56 && geometry
.y
+ geometry
.height
> snap_geometry
.y
- snap
)
57 geometry
.y
= snap_geometry
.y
- geometry
.height
;
63 mouse_snapclienttogeometry_inside(area_t geometry
, area_t snap_geometry
, int snap
)
65 if(abs(geometry
.x
) < snap
+ snap_geometry
.x
&& geometry
.x
> snap_geometry
.x
)
66 geometry
.x
= snap_geometry
.x
;
67 else if(abs((snap_geometry
.x
+ snap_geometry
.width
) - (geometry
.x
+ geometry
.width
))
69 geometry
.x
= snap_geometry
.x
+ snap_geometry
.width
- geometry
.width
;
70 if(abs(geometry
.y
) < snap
+ snap_geometry
.y
&& geometry
.y
> snap_geometry
.y
)
71 geometry
.y
= snap_geometry
.y
;
72 else if(abs((snap_geometry
.y
+ snap_geometry
.height
) - (geometry
.y
+ geometry
.height
))
74 geometry
.y
= snap_geometry
.y
+ snap_geometry
.height
- geometry
.height
;
80 mouse_snapclient(Client
*c
, area_t geometry
)
83 int snap
= globalconf
.screens
[c
->screen
].snap
;
84 area_t snapper_geometry
;
85 area_t screen_geometry
=
86 screen_get_area(c
->screen
,
87 globalconf
.screens
[c
->screen
].statusbar
,
88 &globalconf
.screens
[c
->screen
].padding
);
90 geometry
= titlebar_geometry_add(&c
->titlebar
, geometry
);
91 geometry
.width
+= 2 * c
->border
;
92 geometry
.height
+= 2 * c
->border
;
95 mouse_snapclienttogeometry_inside(geometry
, screen_geometry
, snap
);
97 for(snapper
= globalconf
.clients
; snapper
; snapper
= snapper
->next
)
98 if(snapper
!= c
&& client_isvisible(c
, c
->screen
))
100 snapper_geometry
= snapper
->geometry
;
101 snapper_geometry
.width
+= 2 * c
->border
;
102 snapper_geometry
.height
+= 2 * c
->border
;
103 snapper_geometry
= titlebar_geometry_add(&snapper
->titlebar
,
106 mouse_snapclienttogeometry_outside(geometry
,
111 geometry
.width
-= 2 * c
->border
;
112 geometry
.height
-= 2 * c
->border
;
113 return titlebar_geometry_remove(&c
->titlebar
, geometry
);
117 mouse_resizebar_draw(DrawCtx
*ctx
, style_t style
, SimpleWindow
*sw
, area_t geometry
, int border
)
119 area_t draw_geometry
= { 0, 0, ctx
->width
, ctx
->height
, NULL
, NULL
};
122 snprintf(size
, sizeof(size
), "%dx%d+%d+%d",
123 geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
124 draw_text(ctx
, draw_geometry
, AlignCenter
, style
.font
->height
/ 2, size
, style
);
125 simplewindow_move(sw
,
126 geometry
.x
+ ((2 * border
+ geometry
.width
) - sw
->geometry
.width
) / 2,
127 geometry
.y
+ ((2 * border
+ geometry
.height
) - sw
->geometry
.height
) / 2);
128 simplewindow_refresh_drawable(sw
, sw
->phys_screen
);
131 /** Move the focused window with the mouse.
132 * \param screen Screen ID
134 * \ingroup ui_callback
137 uicb_client_movemouse(int screen
, char *arg
__attribute__ ((unused
)))
139 int ocx
, ocy
, newscreen
;
141 Client
*c
= globalconf
.focus
->client
, *target
;
142 Layout
*layout
= layout_get_current(screen
);
143 SimpleWindow
*sw
= NULL
;
146 xcb_generic_event_t
*ev
= NULL
;
147 xcb_motion_notify_event_t
*ev_motion
= NULL
;
148 xcb_grab_pointer_reply_t
*grab_pointer_r
= NULL
;
149 xcb_query_pointer_reply_t
*query_pointer_r
= NULL
, *mquery_pointer_r
= NULL
;
150 xcb_screen_t
*s
= xcb_aux_get_screen(globalconf
.connection
, c
->phys_screen
);
153 || !(grab_pointer_r
= xcb_grab_pointer_reply(globalconf
.connection
,
154 xcb_grab_pointer(globalconf
.connection
, false,
156 MOUSEMASK
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
,
158 globalconf
.cursor
[CurMove
], XCB_CURRENT_TIME
),
162 p_delete(&grab_pointer_r
);
164 query_pointer_r
= xcb_query_pointer_reply(globalconf
.connection
,
165 xcb_query_pointer_unchecked(globalconf
.connection
,
169 geometry
= c
->geometry
;
174 style
= globalconf
.screens
[c
->screen
].styles
.focus
;
176 if(c
->isfloating
|| layout
->arrange
== layout_floating
)
178 sw
= simplewindow_new(globalconf
.connection
, c
->phys_screen
, 0, 0,
179 draw_textwidth(globalconf
.connection
,
180 globalconf
.default_screen
,
181 globalconf
.screens
[c
->screen
].styles
.focus
.font
,
182 "0000x0000+0000+0000") + style
.font
->height
,
183 1.5 * style
.font
->height
, 0);
185 ctx
= draw_context_new(globalconf
.connection
, sw
->phys_screen
,
186 sw
->geometry
.width
, sw
->geometry
.height
,
188 xutil_map_raised(globalconf
.connection
, sw
->window
);
189 mouse_resizebar_draw(ctx
, style
, sw
, geometry
, c
->border
);
194 /* TODO: need a review
196 * XMaskEvent allows to retrieve only specified events from
197 * the queue and requeue the other events...
199 while((ev
= xcb_poll_for_event(globalconf
.connection
)))
201 switch((ev
->response_type
& 0x7f))
203 case XCB_BUTTON_RELEASE
:
204 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
207 draw_context_delete(&ctx
);
208 simplewindow_delete(&sw
);
211 p_delete(&query_pointer_r
);
214 case XCB_MOTION_NOTIFY
:
215 if(c
->isfloating
|| layout
->arrange
== layout_floating
)
217 ev_motion
= (xcb_motion_notify_event_t
*) ev
;
219 geometry
.x
= ocx
+ (ev_motion
->event_x
- query_pointer_r
->root_x
);
220 geometry
.y
= ocy
+ (ev_motion
->event_y
- query_pointer_r
->root_y
);
222 geometry
= mouse_snapclient(c
, geometry
);
225 mouse_resizebar_draw(ctx
, style
, sw
, c
->geometry
, c
->border
);
227 xcb_aux_sync(globalconf
.connection
);
232 mquery_pointer_r
= xcb_query_pointer_reply(globalconf
.connection
,
233 xcb_query_pointer_unchecked(globalconf
.connection
,
236 if((newscreen
= screen_get_bycoord(globalconf
.screens_info
, c
->screen
,
237 mquery_pointer_r
->root_x
,
238 mquery_pointer_r
->root_y
)) != c
->screen
)
240 move_client_to_screen(c
, newscreen
, true);
241 globalconf
.screens
[c
->screen
].need_arrange
= true;
242 globalconf
.screens
[newscreen
].need_arrange
= true;
245 if((target
= client_get_bywin(globalconf
.clients
, mquery_pointer_r
->child
))
246 && target
!= c
&& !target
->isfloating
)
248 client_list_swap(&globalconf
.clients
, c
, target
);
249 globalconf
.screens
[c
->screen
].need_arrange
= true;
253 p_delete(&mquery_pointer_r
);
256 case XCB_CONFIGURE_REQUEST
:
258 case XCB_MAP_REQUEST
:
259 case XCB_ENTER_NOTIFY
:
262 xcb_handle_event(globalconf
.evenths
, ev
);
270 p_delete(&query_pointer_r
);
273 /** Resize the focused window with the mouse.
274 * \param screen Screen ID
276 * \ingroup ui_callback
279 uicb_client_resizemouse(int screen
, char *arg
__attribute__ ((unused
)))
281 int ocx
= 0, ocy
= 0, n
;
282 xcb_generic_event_t
*ev
= NULL
;
283 xcb_motion_notify_event_t
*ev_motion
= NULL
;
284 Client
*c
= globalconf
.focus
->client
;
285 Tag
**curtags
= tags_get_current(screen
);
286 Layout
*layout
= curtags
[0]->layout
;
287 area_t area
= { 0, 0, 0, 0, NULL
, NULL
}, geometry
= { 0, 0, 0, 0, NULL
, NULL
};
289 SimpleWindow
*sw
= NULL
;
292 xcb_grab_pointer_cookie_t grab_pointer_c
;
293 xcb_grab_pointer_reply_t
*grab_pointer_r
= NULL
;
294 xcb_screen_t
*s
= xcb_aux_get_screen(globalconf
.connection
, c
->phys_screen
);
296 /* only handle floating and tiled layouts */
300 style
= globalconf
.screens
[c
->screen
].styles
.focus
;
302 if(layout
->arrange
== layout_floating
|| c
->isfloating
)
308 sw
= simplewindow_new(globalconf
.connection
, c
->phys_screen
, 0, 0,
309 draw_textwidth(globalconf
.connection
,
310 globalconf
.default_screen
,
311 globalconf
.screens
[c
->screen
].styles
.focus
.font
,
312 "0000x0000+0000+0000") + style
.font
->height
,
313 1.5 * style
.font
->height
, 0);
315 ctx
= draw_context_new(globalconf
.connection
, sw
->phys_screen
,
316 sw
->geometry
.width
, sw
->geometry
.height
,
318 xutil_map_raised(globalconf
.connection
, sw
->window
);
319 mouse_resizebar_draw(ctx
, style
, sw
, geometry
, c
->border
);
321 else if (layout
->arrange
== layout_tile
|| layout
->arrange
== layout_tileleft
322 || layout
->arrange
== layout_tilebottom
|| layout
->arrange
== layout_tiletop
)
324 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
)
325 if(IS_TILED(c
, screen
))
328 if(n
<= curtags
[0]->nmaster
) return;
330 for(c
= globalconf
.clients
; c
&& !IS_TILED(c
, screen
); c
= c
->next
);
333 area
= screen_get_area(screen
,
334 globalconf
.screens
[c
->screen
].statusbar
,
335 &globalconf
.screens
[c
->screen
].padding
);
340 grab_pointer_c
= xcb_grab_pointer(globalconf
.connection
, false, s
->root
,
341 MOUSEMASK
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
,
342 s
->root
, globalconf
.cursor
[CurResize
], XCB_CURRENT_TIME
);
344 if(layout
->arrange
== layout_floating
|| c
->isfloating
)
350 else if (layout
->arrange
== layout_tile
|| layout
->arrange
== layout_tileleft
351 || layout
->arrange
== layout_tilebottom
|| layout
->arrange
== layout_tiletop
)
353 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
)
354 if(IS_TILED(c
, screen
))
357 if(n
<= curtags
[0]->nmaster
) return;
359 for(c
= globalconf
.clients
; c
&& !IS_TILED(c
, screen
); c
= c
->next
);
362 area
= screen_get_area(screen
,
363 globalconf
.screens
[c
->screen
].statusbar
,
364 &globalconf
.screens
[c
->screen
].padding
);
367 if((grab_pointer_r
= xcb_grab_pointer_reply(globalconf
.connection
,
368 grab_pointer_c
, NULL
)) == NULL
)
371 p_delete(&grab_pointer_r
);
373 if(curtags
[0]->layout
->arrange
== layout_tileleft
)
374 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, c
->win
, 0, 0,
375 0, 0, 0, c
->geometry
.height
+ c
->border
- 1);
376 else if(curtags
[0]->layout
->arrange
== layout_tilebottom
)
377 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, c
->win
,
378 0, 0, 0, 0, c
->geometry
.width
+ c
->border
- 1,
379 c
->geometry
.height
+ c
->border
- 1);
380 else if(curtags
[0]->layout
->arrange
== layout_tiletop
)
381 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, c
->win
, 0, 0,
382 0, 0, c
->geometry
.width
+ c
->border
- 1, 0);
384 xcb_warp_pointer(globalconf
.connection
, XCB_NONE
, c
->win
, 0, 0,
385 0, 0, c
->geometry
.width
+ c
->border
- 1,
386 c
->geometry
.height
+ c
->border
- 1);
388 xcb_aux_sync(globalconf
.connection
);
392 /* TODO: need a review
394 * XMaskEvent allows to retrieve only specified events from
395 * the queue and requeue the other events...
397 while((ev
= xcb_poll_for_event(globalconf
.connection
)))
399 switch((ev
->response_type
& 0x7f))
401 case XCB_BUTTON_RELEASE
:
404 draw_context_delete(&ctx
);
405 simplewindow_delete(&sw
);
407 xcb_ungrab_pointer(globalconf
.connection
, XCB_CURRENT_TIME
);
410 case XCB_MOTION_NOTIFY
:
411 ev_motion
= (xcb_motion_notify_event_t
*) ev
;
413 if(layout
->arrange
== layout_floating
|| c
->isfloating
)
415 if((geometry
.width
= ev_motion
->event_x
- ocx
- 2 * c
->border
+ 1) <= 0)
417 if((geometry
.height
= ev_motion
->event_y
- ocy
- 2 * c
->border
+ 1) <= 0)
419 geometry
.x
= c
->geometry
.x
;
420 geometry
.y
= c
->geometry
.y
;
421 client_resize(c
, geometry
, true);
423 mouse_resizebar_draw(ctx
, style
, sw
, c
->geometry
, c
->border
);
424 xcb_aux_sync(globalconf
.connection
);
426 else if(layout
->arrange
== layout_tile
|| layout
->arrange
== layout_tileleft
427 || layout
->arrange
== layout_tiletop
|| layout
->arrange
== layout_tilebottom
)
429 if(layout
->arrange
== layout_tile
)
430 mwfact
= (double) (ev_motion
->event_x
- area
.x
) / area
.width
;
431 else if(curtags
[0]->layout
->arrange
== layout_tileleft
)
432 mwfact
= 1 - (double) (ev_motion
->event_x
- area
.x
) / area
.width
;
433 else if(curtags
[0]->layout
->arrange
== layout_tilebottom
)
434 mwfact
= (double) (ev_motion
->event_y
- area
.y
) / area
.height
;
436 mwfact
= 1 - (double) (ev_motion
->event_y
- area
.y
) / area
.height
;
437 mwfact
= MAX(globalconf
.screens
[screen
].mwfact_lower_limit
,
438 MIN(globalconf
.screens
[screen
].mwfact_upper_limit
, mwfact
));
439 if(fabs(curtags
[0]->mwfact
- mwfact
) >= 0.01)
441 curtags
[0]->mwfact
= mwfact
;
442 globalconf
.screens
[screen
].need_arrange
= true;
448 case XCB_CONFIGURE_REQUEST
:
450 case XCB_MAP_REQUEST
:
453 xcb_handle_event(globalconf
.evenths
, ev
);
463 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80