Clip client rect to monitor bounds while picking a corner for resizing
[dana/openbox.git] / openbox / actions / resize.c
1 #include "openbox/actions.h"
2 #include "openbox/moveresize.h"
3 #include "openbox/client.h"
4 #include "openbox/frame.h"
5 #include "openbox/screen.h"
6 #include "obt/prop.h"
7
8 typedef struct {
9     gboolean corner_specified;
10     guint32 corner;
11 } Options;
12
13 static gpointer setup_func(xmlNodePtr node);
14 static void free_func(gpointer o);
15 static gboolean run_func(ObActionsData *data, gpointer options);
16
17 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
18                            gboolean shaded);
19
20 void action_resize_startup(void)
21 {
22     actions_register("Resize", setup_func, free_func, run_func);
23 }
24
25 static gpointer setup_func(xmlNodePtr node)
26 {
27     xmlNodePtr n;
28     Options *o;
29
30     o = g_slice_new0(Options);
31
32     if ((n = obt_xml_find_node(node, "edge"))) {
33         gchar *s = obt_xml_node_string(n);
34
35         o->corner_specified = TRUE;
36         if (!g_ascii_strcasecmp(s, "top"))
37             o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP);
38         else if (!g_ascii_strcasecmp(s, "bottom"))
39             o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM);
40         else if (!g_ascii_strcasecmp(s, "left"))
41             o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT);
42         else if (!g_ascii_strcasecmp(s, "right"))
43             o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT);
44         else if (!g_ascii_strcasecmp(s, "topleft"))
45             o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT);
46         else if (!g_ascii_strcasecmp(s, "topright"))
47             o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT);
48         else if (!g_ascii_strcasecmp(s, "bottomleft"))
49             o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT);
50         else if (!g_ascii_strcasecmp(s, "bottomright"))
51             o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT);
52         else
53             o->corner_specified = FALSE;
54
55         g_free(s);
56     }
57     return o;
58 }
59
60 static void free_func(gpointer o)
61 {
62     g_slice_free(Options, o);
63 }
64
65 /* Always return FALSE because its not interactive */
66 static gboolean run_func(ObActionsData *data, gpointer options)
67 {
68     Options *o = options;
69
70     if (data->client) {
71         ObClient *c = data->client;
72         guint32 corner;
73
74         if (!data->button)
75             corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD);
76         else if (o->corner_specified)
77             corner = o->corner; /* it was specified in the binding */
78         else
79             corner = pick_corner(data->x, data->y,
80                                  c->frame->area.x, c->frame->area.y,
81                                  /* use the client size because the frame
82                                     can be differently sized (shaded
83                                     windows) and we want this based on the
84                                     clients size */
85                                  c->area.width + c->frame->size.left +
86                                  c->frame->size.right,
87                                  c->area.height + c->frame->size.top +
88                                  c->frame->size.bottom, c->shaded);
89
90         moveresize_start(c, data->x, data->y, data->button, corner);
91     }
92
93     return FALSE;
94 }
95
96 static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
97                            gboolean shaded)
98 {
99     const Rect *full = screen_physical_area_all_monitors();
100     if (cx < full->x) { cw = cw + cx - full->x; cx = full->x; }
101     if (cy < full->y) { ch = ch + cy - full->y; cy = full->y; }
102     if (cx + cw > full->x + full->width) cw = full->x + full->width - cx;
103     if (cy + ch > full->y + full->height) ch = full->y + full->height - cy;
104
105     /* let's make x and y client relative instead of screen relative */
106     x = x - cx;
107     y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
108
109 #define X x*ch/cw
110 #define A -4*X + 7*ch/3
111 #define B  4*X -15*ch/9
112 #define C -X/4 + 2*ch/3
113 #define D  X/4 + 5*ch/12
114 #define E  X/4 +   ch/3
115 #define F -X/4 + 7*ch/12
116 #define G  4*X - 4*ch/3
117 #define H -4*X + 8*ch/3
118 #define a (y > 5*ch/9)
119 #define b (x < 4*cw/9)
120 #define c (x > 5*cw/9)
121 #define d (y < 4*ch/9)
122
123     /*
124       Each of these defines (except X which is just there for fun), represents
125       the equation of a line. The lines they represent are shown in the diagram
126       below. Checking y against these lines, we are able to choose a region
127       of the window as shown.
128
129       +---------------------A-------|-------|-------B---------------------+
130       |                     |A                     B|                     |
131       |                     |A      |       |      B|                     |
132       |                     | A                   B |                     |
133       |                     | A     |       |     B |                     |
134       |                     |  A                 B  |                     |
135       |                     |  A    |       |    B  |                     |
136       |        northwest    |   A     north     B   |   northeast         |
137       |                     |   A   |       |   B   |                     |
138       |                     |    A             B    |                     |
139       C---------------------+----A--+-------+--B----+---------------------D
140       |CCCCCCC              |     A           B     |              DDDDDDD|
141       |       CCCCCCCC      |     A |       | B     |      DDDDDDDD       |
142       |               CCCCCCC      A         B      DDDDDDD               |
143       - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
144       |                     |       b       c       |                     | sh
145       |             west    |       b  move c       |   east              | ad
146       |                     |       b       c       |                     | ed
147       - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - -  -
148       |               EEEEEEE      G         H      FFFFFFF               |
149       |       EEEEEEEE      |     G |       | H     |      FFFFFFFF       |
150       |EEEEEEE              |     G           H     |              FFFFFFF|
151       E---------------------+----G--+-------+--H----+---------------------F
152       |                     |    G             H    |                     |
153       |                     |   G   |       |   H   |                     |
154       |        southwest    |   G     south     H   |   southeast         |
155       |                     |  G    |       |    H  |                     |
156       |                     |  G                 H  |                     |
157       |                     | G     |       |     H |                     |
158       |                     | G                   H |                     |
159       |                     |G      |       |      H|                     |
160       |                     |G                     H|                     |
161       +---------------------G-------|-------|-------H---------------------+
162     */
163
164     if (shaded) {
165         /* for shaded windows, you can only resize west/east and move */
166         if (b)
167             return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT);
168         if (c)
169             return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT);
170         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE);
171     }
172
173     if (y < A && y >= C)
174         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT);
175     else if (y >= A && y >= B && a)
176         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP);
177     else if (y < B && y >= D)
178         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT);
179     else if (y < C && y >= E && b)
180         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT);
181     else if (y < D && y >= F && c)
182         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT);
183     else if (y < E && y >= G)
184         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT);
185     else if (y < G && y < H && d)
186         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM);
187     else if (y >= H && y < F)
188         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT);
189     else
190         return OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE);
191
192 #undef X
193 #undef A
194 #undef B
195 #undef C
196 #undef D
197 #undef E
198 #undef F
199 #undef G
200 #undef H
201 #undef a
202 #undef b
203 #undef c
204 #undef d
205 }