]> git.decadent.org.uk Git - ion3.git/blob - ioncore/kbresize.c
b4dbc7b058002d186c1d8f7d82e9dd76aa394948
[ion3.git] / ioncore / kbresize.c
1 /*
2  * ion/ioncore/kbresize.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <math.h>
13 #include <sys/time.h>
14 #include <time.h>
15 #include <limits.h>
16
17 #include <libtu/minmax.h>
18
19 #include <libmainloop/signal.h>
20
21 #include "global.h"
22 #include "resize.h"
23 #include "kbresize.h"
24 #include "grab.h"
25 #include "binding.h"
26 #include "focus.h"
27 #include "bindmaps.h"
28
29
30 /*{{{ Resize accelerator */
31
32
33 static struct timeval last_action_tv={-1, 0};
34 static struct timeval last_update_tv={-1, 0};
35 static int last_accel_mode=0;
36 static double accel=1, accelinc=30, accelmax=100*100;
37 static long actmax=200, uptmin=50;
38 static int resize_delay=CF_RESIZE_DELAY;
39
40
41 static void accel_reset()
42 {
43     last_accel_mode=0;
44     accel=1.0;
45     last_action_tv.tv_sec=-1;
46     last_action_tv.tv_usec=-1;
47 }
48
49
50 void ioncore_set_moveres_accel(ExtlTab tab)
51 {
52     int t_max, t_min, rd;
53     double step, maxacc;
54     
55     if(extl_table_gets_i(tab, "kbresize_t_max", &t_max))
56        actmax=(t_max>0 ? t_max : INT_MAX);
57     if(extl_table_gets_i(tab, "kbresize_t_min", &t_min))
58        uptmin=(t_min>0 ? t_min : INT_MAX);
59     if(extl_table_gets_d(tab, "kbresize_step", &step))
60        accelinc=(step>0 ? step : 1);
61     if(extl_table_gets_d(tab, "kbresize_maxacc", &maxacc))
62        accelmax=(maxacc>0 ? maxacc*maxacc : 1);
63     if(extl_table_gets_i(tab, "kbresize_delay", &rd))
64         resize_delay=maxof(0, rd);
65 }
66
67
68 void ioncore_get_moveres_accel(ExtlTab tab)
69 {
70     extl_table_sets_i(tab, "kbresize_t_max", actmax),
71     extl_table_sets_i(tab, "kbresize_t_min", uptmin);
72     extl_table_sets_d(tab, "kbresize_step", accelinc);
73     extl_table_sets_d(tab, "kbresize_maxacc", accelmax);
74     extl_table_sets_d(tab, "kbresize_delay", resize_delay);
75 }
76
77
78 static int sign(int x)
79 {
80     return (x>0 ? 1 : (x<0 ? -1 : 0));
81 }
82
83
84 static long tvdiffmsec(struct timeval *tv1, struct timeval *tv2)
85 {
86     double t1=1000*(double)tv1->tv_sec+(double)tv1->tv_usec/1000;
87     double t2=1000*(double)tv2->tv_sec+(double)tv2->tv_usec/1000;
88     
89     return (int)(t1-t2);
90 }
91
92 #define SIGN_NZ(X) ((X) < 0 ? -1 : 1)
93
94 static double max(double a, double b)
95 {
96     return (a<b ? b : a);
97 }
98
99 void moveresmode_accel(WMoveresMode *mode, int *wu, int *hu, int accel_mode)
100 {
101     struct timeval tv;
102     long adiff, udiff;
103     
104     gettimeofday(&tv, NULL);
105     
106     adiff=tvdiffmsec(&tv, &last_action_tv);
107     udiff=tvdiffmsec(&tv, &last_update_tv);
108     
109     if(last_accel_mode==accel_mode && adiff<actmax){
110         if(udiff>uptmin){
111             accel+=accelinc;
112             if(accel>accelmax)
113                 accel=accelmax;
114             last_update_tv=tv;
115         }
116     }else{
117         accel=1.0;
118         last_update_tv=tv;
119     }
120     
121     last_accel_mode=accel_mode;
122     last_action_tv=tv;
123     
124     if(*wu!=0)
125         *wu=(*wu)*ceil(sqrt(accel)/abs(*wu));
126     if(*hu!=0)
127         *hu=(*hu)*ceil(sqrt(accel)/abs(*hu));
128 }
129
130
131 /*}}}*/
132
133
134 /*{{{ Keyboard resize handler */
135
136
137 static ExtlExportedFn *moveres_safe_fns[]={
138     (ExtlExportedFn*)&moveresmode_resize,
139     (ExtlExportedFn*)&moveresmode_move,
140     (ExtlExportedFn*)&moveresmode_rqgeom_extl,
141     (ExtlExportedFn*)&moveresmode_geom,
142     (ExtlExportedFn*)&moveresmode_finish,
143     (ExtlExportedFn*)&moveresmode_cancel,
144     NULL
145 };
146
147 static ExtlSafelist moveres_safelist=EXTL_SAFELIST_INIT(moveres_safe_fns);
148
149
150 static bool resize_handler(WRegion *reg, XEvent *xev)
151 {
152     XKeyEvent *ev=&xev->xkey;
153     WBinding *binding=NULL;
154     WBindmap **bindptr;
155     WMoveresMode *mode;
156     
157     if(ev->type==KeyRelease)
158         return FALSE;
159     
160     if(reg==NULL)
161         return FALSE;
162     
163     mode=moveres_mode(reg);
164     
165     if(mode==NULL)
166         return FALSE;
167     
168     binding=bindmap_lookup_binding(ioncore_moveres_bindmap, 
169                                    BINDING_KEYPRESS,
170                                    ev->state, ev->keycode);
171     
172     if(!binding)
173         return FALSE;
174     
175     if(binding!=NULL){
176         extl_protect(&moveres_safelist);
177         extl_call(binding->func, "oo", NULL, mode, reg);
178         extl_unprotect(&moveres_safelist);
179     }
180     
181     return (moveres_mode(reg)==NULL);
182 }
183
184
185 /*}}}*/
186
187
188 /*{{{ Resize timer */
189
190
191 static WTimer *resize_timer=NULL;
192
193
194 static void tmr_end_resize(WTimer *unused, WMoveresMode *mode)
195 {
196     if(mode!=NULL)
197         moveresmode_cancel(mode);
198 }
199
200
201 static bool setup_resize_timer(WMoveresMode *mode)
202 {
203     if(resize_timer==NULL){
204         resize_timer=create_timer();
205         if(resize_timer==NULL)
206             return FALSE;
207     }
208     
209     timer_set(resize_timer, resize_delay, 
210               (WTimerHandler*)tmr_end_resize, (Obj*)mode);
211     
212     return TRUE;
213 }
214
215
216 static void reset_resize_timer()
217 {
218     if(resize_timer!=NULL){
219         timer_reset(resize_timer);
220         destroy_obj((Obj*)resize_timer);
221         resize_timer=NULL;
222     }
223 }
224
225
226 /*}}}*/
227
228
229 /*{{{ Misc. */
230
231
232 static int limit_and_encode_mode(int *left, int *right, 
233                                  int *top, int *bottom)
234 {
235     *left=sign(*left);
236     *right=sign(*right);
237     *top=sign(*top);
238     *bottom=sign(*bottom);
239
240     return (*left)+(*right)*3+(*top)*9+(*bottom)*27;
241 }
242
243
244 static void resize_units(WMoveresMode *mode, int *wret, int *hret)
245 {
246     WSizeHints *h=&(mode->hints);
247     *wret=1;
248     *hret=1;
249     if(h->inc_set && (h->width_inc>1 || h->height_inc>1)){
250         *wret=h->width_inc;
251         *hret=h->height_inc;
252     }
253 }
254
255
256 /*}}}*/
257
258
259 /*{{{ Keyboard resize interface */
260
261
262 /*EXTL_DOC
263  * Shrink or grow resize mode target one step in each direction.
264  * Acceptable values for the parameters \var{left}, \var{right}, \var{top}
265  * and \var{bottom} are as follows: -1: shrink along,
266  * 0: do not change, 1: grow along corresponding border.
267  */
268 EXTL_EXPORT_MEMBER
269 void moveresmode_resize(WMoveresMode *mode, 
270                         int left, int right, int top, int bottom)
271 {
272     int wu=0, hu=0;
273     int accel_mode=0;
274     
275     if(!setup_resize_timer(mode))
276         return;
277     
278     accel_mode=3*limit_and_encode_mode(&left, &right, &top, &bottom);
279     resize_units(mode, &wu, &hu);
280     moveresmode_accel(mode, &wu, &hu, accel_mode);
281
282     moveresmode_delta_resize(mode, -left*wu, right*wu, -top*hu, bottom*hu, 
283                              NULL);
284 }
285
286
287 /*EXTL_DOC
288  * Move resize mode target one step:
289  *
290  * \begin{tabular}{rl}
291  * \hline
292  * \var{horizmul}/\var{vertmul} & effect \\\hline
293  * -1 & Move left/up \\
294  * 0 & No effect \\
295  * 1 & Move right/down \\
296  * \end{tabular}
297  */
298 EXTL_EXPORT_MEMBER
299 void moveresmode_move(WMoveresMode *mode, int horizmul, int vertmul)
300 {
301     int accel_mode=0, dummy=0;
302
303     if(!setup_resize_timer(mode))
304         return;
305     
306     accel_mode=1+3*limit_and_encode_mode(&horizmul, &vertmul, &dummy, &dummy);
307     moveresmode_accel(mode, &horizmul, &vertmul, accel_mode);
308
309     moveresmode_delta_resize(mode, horizmul, horizmul, vertmul, vertmul, 
310                              NULL);
311 }
312
313
314 /*EXTL_DOC
315  * Request exact geometry in move/resize mode. For details on parameters,
316  * see \fnref{WRegion.rqgeom}.
317  */
318 EXTL_EXPORT_AS(WMoveresMode, rqgeom)
319 ExtlTab moveresmode_rqgeom_extl(WMoveresMode *mode, ExtlTab g)
320 {
321     WRQGeomParams rq=RQGEOMPARAMS_INIT;
322     WRectangle res;
323     
324     rqgeomparams_from_table(&rq, &mode->geom, g);
325     
326     moveresmode_rqgeom(mode, &rq, &res);
327     
328     return extl_table_from_rectangle(&res);
329 }
330
331 /*EXTL_DOC
332  * Returns current geometry.
333  */
334 EXTL_EXPORT_MEMBER
335 ExtlTab moveresmode_geom(WMoveresMode *mode)
336 {
337     return extl_table_from_rectangle(&mode->geom);
338 }
339
340
341 /*EXTL_DOC
342  * Return from move/resize mode and apply changes unless opaque
343  * move/resize is enabled.
344  */
345 EXTL_EXPORT_MEMBER
346 void moveresmode_finish(WMoveresMode *mode)
347 {
348     WRegion *reg=moveresmode_target(mode);
349     if(moveresmode_do_end(mode, TRUE)){
350         reset_resize_timer();
351         region_warp(reg);
352         ioncore_grab_remove(resize_handler);
353     }
354 }
355
356
357 /*EXTL_DOC
358  * Return from move/resize cancelling changes if opaque
359  * move/resize has not been enabled.
360  */
361 EXTL_EXPORT_MEMBER
362 void moveresmode_cancel(WMoveresMode *mode)
363 {
364     WRegion *reg=moveresmode_target(mode);
365     if(moveresmode_do_end(mode, FALSE)){
366         reset_resize_timer();
367         region_warp(reg);
368         ioncore_grab_remove(resize_handler);
369     }
370 }
371
372
373 static void cancel_moveres(WRegion *reg)
374 {
375     WMoveresMode *mode=moveres_mode(reg);
376     if(mode!=NULL)
377         moveresmode_cancel(mode);
378 }
379
380     
381 /*EXTL_DOC
382  * Enter move/resize mode for \var{reg}. The bindings set with
383  * \fnref{ioncore.set_bindings} for \type{WMoveresMode} are used in 
384  * this mode. Of the functions exported by the Ion C core, only
385  * \fnref{WMoveresMode.resize}, \fnref{WMoveresMode.move}, 
386  * \fnref{WMoveresMode.cancel} and \fnref{WMoveresMode.end} are
387  * allowed to be called while in this mode.
388  */
389 EXTL_EXPORT_MEMBER
390 WMoveresMode *region_begin_kbresize(WRegion *reg)
391 {
392     WMoveresMode *mode=region_begin_resize(reg, NULL, FALSE);
393
394     if(mode==NULL)
395         return NULL;
396     
397     if(!setup_resize_timer(mode))
398         return NULL;
399
400     accel_reset();
401     
402     ioncore_grab_establish(reg, resize_handler,
403                            (GrabKilledHandler*)cancel_moveres, 0);
404     
405     return mode;
406 }
407
408
409 /*}}}*/