fixed focus: get_window_at_pos now searches back to front in root_window
[winnie] / src / wm.cc
1 #include <algorithm>
2 #include <stdexcept>
3 #include <stdio.h>      // TODO
4
5 #include "gfx.h"
6 #include "wm.h"
7 #include "window.h"
8 #include "mouse.h"
9 #include "mouse_cursor.h"
10
11 WindowManager *wm;
12 static WindowManager wminst;
13
14 static void display(Window *win);
15 static void mouse(Window *win, int bn, bool pressed, int x, int y);
16 static void motion(Window *win, int x, int y);
17
18 void WindowManager::create_frame(Window *win)
19 {
20         Window *frame = new Window;
21         Window *parent = win->get_parent();
22
23         frame->set_display_callback(display);
24         frame->set_mouse_button_callback(mouse);
25         frame->set_mouse_motion_callback(motion);
26         frame->set_focusable(false);
27         frame->add_child(win);
28
29         windows.push_back(frame);
30
31         Rect win_rect = win->get_rect();
32         frame->move(win_rect.x - frame_thickness,
33                         win_rect.y - frame_thickness - titlebar_thickness);
34         frame->resize(win_rect.width + frame_thickness * 2,
35                         win_rect.height + frame_thickness * 2 + titlebar_thickness);
36
37         win->move(frame_thickness, frame_thickness + titlebar_thickness);
38         parent->add_child(frame);
39 }
40
41 void WindowManager::destroy_frame(Window *win)
42 {
43         Window *frame = win->parent;
44         if(!frame) {
45                 return;
46         }
47
48         if(grab_win == win) {
49                 release_mouse();
50         }
51
52         std::list<Window*>::iterator it;
53         it = std::find(windows.begin(), windows.end(), frame);
54         if(it != windows.end()) {
55                 root_win->add_child(win);
56                 windows.erase(it);
57                 delete frame;
58         }
59 }
60
61 WindowManager::WindowManager()
62 {
63         if(!wm) {
64                 wm = this;
65         } else {
66                 throw std::runtime_error("Trying to create a second instance of WindowManager!\n");
67         }
68
69         root_win = new Window;
70         root_win->resize(get_screen_size().width, get_screen_size().height);
71         root_win->move(0, 0);
72         root_win->set_managed(false);
73
74         focused_win = 0;
75
76         bg_color[0] = 210;
77         bg_color[1] = 106;
78         bg_color[2] = 106;
79
80         frame_thickness = 8;
81         titlebar_thickness = 16;
82
83         frame_fcolor[0] = frame_fcolor[1] = frame_fcolor[2] = 0;
84         frame_ucolor[0] = frame_ucolor[1] = frame_ucolor[2] = 255;
85
86         mouse_cursor.set_image(mouse_cursor_width, mouse_cursor_height);
87         unsigned char *pixels = mouse_cursor.get_image();
88
89         for(int i=0; i<mouse_cursor_height; i++) {
90                 for(int j=0; j<mouse_cursor_width; j++) {
91                         int val = mouse_cursor_bw[i * mouse_cursor_width + j];
92                         *pixels++ = val;
93                         *pixels++ = val;
94                         *pixels++ = val;
95                         *pixels++ = 255;
96                 }
97         }
98 }
99
100 WindowManager::~WindowManager()
101 {
102         delete root_win;
103 }
104
105 void WindowManager::invalidate_region(const Rect &rect)
106 {
107         dirty_rects.push_back(rect);
108 }
109
110 void WindowManager::process_windows()
111 {
112         if(dirty_rects.empty()) {
113                 return;
114         }
115
116         std::list<Rect>::iterator drit = dirty_rects.begin();
117         Rect uni = *drit++;
118         while(drit != dirty_rects.end()) {
119                 uni = rect_union(uni, *drit++);
120         }
121         dirty_rects.clear();
122
123         wait_vsync();
124
125         fill_rect(uni, bg_color[0], bg_color[1], bg_color[2]);
126
127         root_win->draw_children(uni);
128
129         // draw mouse cursor
130         int mouse_x, mouse_y;
131         get_pointer_pos(&mouse_x, &mouse_y);
132
133         blit_key(mouse_cursor.get_image(), mouse_cursor.get_rect(),
134                         get_framebuffer(), get_screen_size(), mouse_x, mouse_y,
135                         0, 0, 0);
136
137         Rect mouse_rect = {mouse_x, mouse_y, mouse_cursor.get_width(), mouse_cursor.get_height()};
138         invalidate_region(mouse_rect);
139
140         gfx_update();
141 }
142
143 void WindowManager::add_window(Window *win)
144 {
145         if(!win || win == root_win) {
146                 return;
147         }
148
149         root_win->add_child(win);
150
151         if(windows.empty()) {
152                 focused_win = win;
153         }
154
155         if(win->get_managed()) {
156                 create_frame(win);
157         }
158
159         windows.push_back(win);
160 }
161
162 void WindowManager::remove_window(Window *win)
163 {
164         std::list<Window*>::iterator it;
165         it = std::find(windows.begin(), windows.end(), win);
166
167         if(it != windows.end()) {
168                 windows.erase(it);
169         }
170 }
171
172 void WindowManager::set_focused_window(Window *win)
173 {
174         if(win && win == focused_win) {
175                 return;
176         }
177
178         Window *parent;
179         if(focused_win) {
180                 // invalidate the frame (if any)
181                 parent = focused_win->get_parent();
182                 if(parent && parent != root_win) {
183                         parent->invalidate();
184                         fill_rect(parent->get_absolute_rect(), frame_ucolor[0], frame_ucolor[1], frame_ucolor[2]);
185                 }
186         }
187
188         if(!win) {
189                 focused_win = 0;
190                 return;
191         }
192
193         if(win->get_focusable()) {
194                 focused_win = win;
195                 parent = focused_win->get_parent();
196                 fill_rect(parent->get_absolute_rect(), frame_fcolor[0], frame_fcolor[1], frame_fcolor[2]);
197                 return;
198         }
199
200         Window **children = win->get_children();
201         for(int i=0; i<win->get_children_count(); i++) {
202                 if(children[0]->get_focusable()) {
203                         set_focused_window(children[0]);
204                         fill_rect(win->get_absolute_rect(), frame_fcolor[0], frame_fcolor[1], frame_fcolor[2]);
205                         return;
206                 }
207         }
208
209         focused_win = 0;
210 }
211
212 const Window *WindowManager::get_focused_window() const
213 {
214         return focused_win;
215 }
216
217 Window *WindowManager::get_focused_window()
218 {
219         return focused_win;
220 }
221
222 Window *WindowManager::get_window_at_pos(int pointer_x, int pointer_y)
223 {
224         Window *root_win = wm->get_root_window();
225         Window **children = root_win->get_children();
226         for(int i=root_win->get_children_count() - 1; i>=0; i--) {
227                 if(children[i]->contains_point(pointer_x, pointer_y)) {
228                         return children[i];
229                 }
230         }
231
232         return 0;
233 }
234
235 Window *WindowManager::get_root_window() const
236 {
237         return root_win;
238 }
239
240 void WindowManager::set_focused_frame_color(int r, int g, int b)
241 {
242         frame_fcolor[0] = r;
243         frame_fcolor[1] = g;
244         frame_fcolor[2] = b;
245 }
246
247 void WindowManager::get_focused_frame_color(int *r, int *g, int *b)
248 {
249         *r = frame_fcolor[0];
250         *g = frame_fcolor[1];
251         *b = frame_fcolor[2];
252 }
253
254 void WindowManager::set_unfocused_frame_color(int r, int g, int b)
255 {
256         frame_ucolor[0] = r;
257         frame_ucolor[1] = g;
258         frame_ucolor[2] = b;
259 }
260
261 Window *WindowManager::get_grab_window() const
262 {
263         return grab_win;
264 }
265
266 void WindowManager::grab_mouse(Window *win)
267 {
268         grab_win = win;
269 }
270
271 void WindowManager::release_mouse()
272 {
273         grab_win = 0;
274 }
275
276 void WindowManager::raise_window(Window *win)
277 {
278         if(!win) {
279                 return;
280         }
281
282         Window *parent = win->get_parent();
283         if(parent != root_win) {
284                 if(parent->get_parent() == root_win) {
285                         win = parent;
286                 }
287                 else {
288                         return;
289                 }
290         }
291
292         root_win->remove_child(win);
293         root_win->add_child(win);
294 }
295
296 void WindowManager::sink_window(Window *win)
297 {
298         if(!win) {
299                 return;
300         }
301
302         std::list<Window*>::iterator it;
303         it = std::find(windows.begin(), windows.end(), win);
304         if(it != windows.end()) {
305                 windows.erase(it);
306                 windows.push_front(win);
307         }
308 }
309
310 static void display(Window *win)
311 {
312         //frame display:
313         Window **children = win->get_children();
314         for(int i=0; i<win->get_children_count(); i++) {
315                 if(children[0] == wm->get_focused_window()) {
316                         int r, g, b;
317                         wm->get_focused_frame_color(&r, &g, &b);
318                         fill_rect(win->get_absolute_rect(), r, g, b);
319                         return;
320                 }
321         }
322
323         fill_rect(win->get_absolute_rect(), 74, 175, 198);
324 }
325
326 static int prev_x, prev_y;
327
328 static void mouse(Window *win, int bn, bool pressed, int x, int y)
329 {
330         if(bn == 0) {
331                 if(pressed) {
332                         wm->grab_mouse(win);
333                         wm->raise_window(win);
334                         prev_x = x;
335                         prev_y = y;
336                 }
337                 else {
338                         wm->release_mouse();
339                 }
340         }
341 }
342
343 static void motion(Window *win, int x, int y)
344 {
345         int left_bn = get_button(0);
346         int right_button = get_button(2);
347
348         if(left_bn) {
349                 int dx = x - prev_x;
350                 int dy = y - prev_y;
351                 prev_x = x - dx;
352                 prev_y = y - dy;
353
354                 Rect rect = win->get_rect();
355                 win->move(rect.x + dx, rect.y + dy);
356         }
357 }