created libwinnie (library), winnie (the server application) and clients
[winnie] / libwinnie / src / wm.cc
1 /*
2 winnie - an experimental window system
3
4 Copyright (C) 2013 Eleni Maria Stea
5
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 3 of the License, or
9 (at your option) any later version.
10
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.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19 Author: Eleni Maria Stea <elene.mst@gmail.com>
20 */
21
22 #include <algorithm>
23 #include <limits.h>
24 #include <stdexcept>
25 #include <stdio.h>      // TODO
26
27 #include "gfx.h"
28 #include "mouse.h"
29 #include "mouse_cursor.h"
30 #include "shalloc.h"
31 #include "text.h"
32 #include "window.h"
33 #include "winnie.h"
34 #include "wm.h"
35
36 #define DCLICK_INTERVAL 400
37
38 WindowManager *wm;
39
40 static void display(Window *win);
41 static void mouse(Window *win, int bn, bool pressed, int x, int y);
42 static void motion(Window *win, int x, int y);
43
44 bool init_window_manager()
45 {
46         void *wm_mem;
47         if(!(wm_mem = sh_malloc(sizeof *wm))) {
48                 return false;
49         }
50
51         wm = new (wm_mem) WindowManager; 
52
53         get_subsys()->wm_offset = (int)((char*)wm - (char*)get_pool());
54
55         return true;
56 }
57
58 void destroy_window_manager()
59 {
60         wm->~WindowManager();
61         sh_free(wm);
62 }
63
64 void WindowManager::create_frame(Window *win)
65 {
66         Window *frame = new Window;
67         Window *parent = win->get_parent();
68
69         frame->set_display_callback(display);
70         frame->set_mouse_button_callback(mouse);
71         frame->set_mouse_motion_callback(motion);
72         frame->set_focusable(false);
73         frame->add_child(win);
74
75         windows.push_back(frame);
76
77         Rect win_rect = win->get_rect();
78         frame->move(win_rect.x - frame_thickness,
79                         win_rect.y - frame_thickness - titlebar_thickness);
80         frame->resize(win_rect.width + frame_thickness * 2,
81                         win_rect.height + frame_thickness * 2 + titlebar_thickness);
82
83         win->move(frame_thickness, frame_thickness + titlebar_thickness);
84         parent->add_child(frame);
85 }
86
87 void WindowManager::destroy_frame(Window *win)
88 {
89         Window *frame = win->parent;
90         if(!frame) {
91                 return;
92         }
93
94         if(grab_win == win) {
95                 release_mouse();
96         }
97
98         std::list<Window*>::iterator it;
99         it = std::find(windows.begin(), windows.end(), frame);
100         if(it != windows.end()) {
101                 root_win->add_child(win);
102                 windows.erase(it);
103                 delete frame;
104         }
105 }
106
107 WindowManager::WindowManager()
108 {
109         if(!wm) {
110                 wm = this;
111         } else {
112                 throw std::runtime_error("Trying to create a second instance of WindowManager!\n");
113         }
114
115         root_win = new Window;
116         root_win->resize(get_screen_size().width, get_screen_size().height);
117         root_win->move(0, 0);
118         root_win->set_managed(false);
119
120         grab_win = 0;
121         focused_win = 0;
122         background = 0;
123
124         bg_color[0] = 210;
125         bg_color[1] = 106;
126         bg_color[2] = 106;
127
128         frame_thickness = 8;
129         titlebar_thickness = 16;
130
131         set_focused_frame_color(0, 0, 0);
132         set_unfocused_frame_color(200, 200, 200);
133
134         mouse_cursor.set_image(mouse_cursor_width, mouse_cursor_height);
135         unsigned char *pixels = mouse_cursor.get_image();
136
137         for(int i=0; i<mouse_cursor_height; i++) {
138                 for(int j=0; j<mouse_cursor_width; j++) {
139                         int val = mouse_cursor_bw[i * mouse_cursor_width + j];
140                         *pixels++ = val;
141                         *pixels++ = val;
142                         *pixels++ = val;
143                         *pixels++ = 255;
144                 }
145         }
146 }
147
148 WindowManager::~WindowManager()
149 {
150         delete root_win;
151 }
152
153 void WindowManager::invalidate_region(const Rect &rect)
154 {
155         dirty_rects.push_back(rect);
156 }
157
158 void WindowManager::process_windows()
159 {
160         if(dirty_rects.empty()) {
161                 return;
162         }
163
164         std::list<Rect>::iterator drit = dirty_rects.begin();
165         Rect uni = *drit++;
166         while(drit != dirty_rects.end()) {
167                 uni = rect_union(uni, *drit++);
168         }
169         dirty_rects.clear();
170
171         wait_vsync();
172
173         if(!background) {
174                 fill_rect(uni, bg_color[0], bg_color[1], bg_color[2]);
175         }
176         else {
177                 blit(background->pixels, Rect(0, 0, background->width, background->height),
178                                 get_framebuffer(), get_screen_size(), 0, 0);
179         }
180
181         root_win->draw_children(uni);
182
183         // draw mouse cursor
184         int mouse_x, mouse_y;
185         get_pointer_pos(&mouse_x, &mouse_y);
186
187         blit_key(mouse_cursor.get_image(), mouse_cursor.get_rect(),
188                         get_framebuffer(), get_screen_size(), mouse_x, mouse_y,
189                         0, 0, 0);
190
191         Rect mouse_rect(mouse_x, mouse_y, mouse_cursor.get_width(), mouse_cursor.get_height());
192         invalidate_region(mouse_rect);
193
194         gfx_update(uni);
195 }
196
197 void WindowManager::add_window(Window *win)
198 {
199         if(!win || win == root_win) {
200                 return;
201         }
202
203         root_win->add_child(win);
204
205         if(windows.empty()) {
206                 focused_win = win;
207         }
208
209         if(win->get_managed()) {
210                 create_frame(win);
211         }
212
213         windows.push_back(win);
214 }
215
216 void WindowManager::remove_window(Window *win)
217 {
218         std::list<Window*>::iterator it;
219         it = std::find(windows.begin(), windows.end(), win);
220
221         if(it != windows.end()) {
222                 windows.erase(it);
223         }
224 }
225
226 void WindowManager::set_focused_window(Window *win)
227 {
228         if(win && win == focused_win) {
229                 return;
230         }
231
232         Window *parent;
233         if(focused_win) {
234                 // invalidate the frame (if any)
235                 parent = focused_win->get_parent();
236                 if(parent && parent != root_win) {
237                         parent->invalidate();
238                         fill_rect(parent->get_absolute_rect(), frame_ucolor[0], frame_ucolor[1], frame_ucolor[2]);
239                 }
240         }
241
242         if(!win) {
243                 focused_win = 0;
244                 return;
245         }
246
247         if(win->get_focusable()) {
248                 focused_win = win;
249                 parent = focused_win->get_parent();
250                 fill_rect(parent->get_absolute_rect(), frame_fcolor[0], frame_fcolor[1], frame_fcolor[2]);
251                 return;
252         }
253
254         Window **children = win->get_children();
255         for(int i=0; i<win->get_children_count(); i++) {
256                 if(children[0]->get_focusable()) {
257                         set_focused_window(children[0]);
258                         fill_rect(win->get_absolute_rect(), frame_fcolor[0], frame_fcolor[1], frame_fcolor[2]);
259                         return;
260                 }
261         }
262
263         focused_win = 0;
264 }
265
266 const Window *WindowManager::get_focused_window() const
267 {
268         return focused_win;
269 }
270
271 Window *WindowManager::get_focused_window()
272 {
273         return focused_win;
274 }
275
276 Window *WindowManager::get_window_at_pos(int pointer_x, int pointer_y)
277 {
278         Window *root_win = wm->get_root_window();
279         Window **children = root_win->get_children();
280         for(int i=root_win->get_children_count() - 1; i>=0; i--) {
281                 if(children[i]->contains_point(pointer_x, pointer_y)) {
282                         return children[i];
283                 }
284         }
285
286         return 0;
287 }
288
289 Window *WindowManager::get_root_window() const
290 {
291         return root_win;
292 }
293
294 void WindowManager::set_focused_frame_color(int r, int g, int b)
295 {
296         frame_fcolor[0] = r;
297         frame_fcolor[1] = g;
298         frame_fcolor[2] = b;
299 }
300
301 void WindowManager::get_focused_frame_color(int *r, int *g, int *b) const
302 {
303         *r = frame_fcolor[0];
304         *g = frame_fcolor[1];
305         *b = frame_fcolor[2];
306 }
307
308 void WindowManager::set_unfocused_frame_color(int r, int g, int b)
309 {
310         frame_ucolor[0] = r;
311         frame_ucolor[1] = g;
312         frame_ucolor[2] = b;
313 }
314
315 void WindowManager::get_unfocused_frame_color(int *r, int *g, int *b) const
316 {
317         *r = frame_ucolor[0];
318         *g = frame_ucolor[1];
319         *b = frame_ucolor[2];
320 }
321
322 void WindowManager::set_background(const Pixmap *pixmap)
323 {
324         if(background) {
325                 delete background;
326         }
327
328         if(pixmap) {
329                 background = new Pixmap(*pixmap);
330         }
331         else {
332                 background = 0;
333         }
334 }
335
336 const Pixmap *WindowManager::get_background() const
337 {
338         return background;
339 }
340
341 Window *WindowManager::get_grab_window() const
342 {
343         return grab_win;
344 }
345
346 void WindowManager::grab_mouse(Window *win)
347 {
348         grab_win = win;
349 }
350
351 void WindowManager::release_mouse()
352 {
353         grab_win = 0;
354 }
355
356 void WindowManager::raise_window(Window *win)
357 {
358         if(!win) {
359                 return;
360         }
361
362         Window *parent = win->get_parent();
363         if(parent != root_win) {
364                 if(parent->get_parent() == root_win) {
365                         win = parent;
366                 }
367                 else {
368                         return;
369                 }
370         }
371
372         root_win->remove_child(win);
373         root_win->add_child(win);
374 }
375
376 void WindowManager::sink_window(Window *win)
377 {
378         if(!win) {
379                 return;
380         }
381
382         std::list<Window*>::iterator it;
383         it = std::find(windows.begin(), windows.end(), win);
384         if(it != windows.end()) {
385                 windows.erase(it);
386                 windows.push_front(win);
387         }
388 }
389
390 void WindowManager::maximize_window(Window *win)
391 {
392         win->normal_rect = win->rect;
393         
394         Rect rect = get_screen_size();
395
396         Window *frame;
397         if((frame = win->get_parent())) {
398                 frame->normal_rect = frame->rect;
399                 frame->resize(rect.width, rect.height);
400                 frame->move(rect.x, rect.y);
401
402                 rect.width -= frame_thickness * 2;
403                 rect.height -= frame_thickness * 2 + titlebar_thickness;
404         }
405         else {
406                 win->move(0, 0);
407         }
408
409         win->resize(rect.width, rect.height);
410         win->set_state(Window::STATE_MAXIMIZED);
411 }
412
413 void WindowManager::unmaximize_window(Window *win)
414 {
415         win->resize(win->normal_rect.width, win->normal_rect.height);
416         win->move(win->normal_rect.x, win->normal_rect.y);
417
418         Window *frame;
419         if((frame = win->get_parent())) {
420                 frame->resize(frame->normal_rect.width, frame->normal_rect.height);
421                 frame->move(frame->normal_rect.x, frame->normal_rect.y);
422         }
423
424         win->set_state(Window::STATE_NORMAL);
425 }
426
427 static void display(Window *win)
428 {
429         //frame display:
430         Window *child = win->get_children()[0];
431         int r, g, b;
432         Rect abs_rect = win->get_absolute_rect();
433
434         //TODO 5 not hardcoded
435         set_text_position(abs_rect.x + 5, abs_rect.y + 15);
436         set_text_color(255, 255, 255);
437
438         if(child == wm->get_focused_window()) {
439                 wm->get_focused_frame_color(&r, &g, &b);
440                 fill_rect(abs_rect, r, g, b);
441         }
442         else {
443                 wm->get_unfocused_frame_color(&r, &g, &b);
444                 fill_rect(win->get_absolute_rect(), r, g, b);
445         }
446
447         draw_text(child->get_title());
448 }
449
450 static int prev_x, prev_y;
451
452 static void mouse(Window *win, int bn, bool pressed, int x, int y)
453 {
454         static long last_click = 0;
455
456         if(bn == 0) {
457                 if(pressed) {   
458                         wm->grab_mouse(win);
459                         wm->raise_window(win);
460                         prev_x = x;
461                         prev_y = y;
462                 }
463                 else {
464                         long time = winnie_get_time();
465                         if((time - last_click) < DCLICK_INTERVAL) {
466                                 Window *child = win->get_children()[0];
467                                 Window::State state = child->get_state();
468                                 if(state == Window::STATE_MAXIMIZED) {
469                                         wm->unmaximize_window(child);
470                                 }
471                                 else if(state == Window::STATE_NORMAL) {
472                                         wm->maximize_window(child);
473                                 }
474                         }
475                         last_click = time;
476
477                         wm->release_mouse();
478                 }
479         }
480 }
481
482 static void motion(Window *win, int x, int y)
483 {
484         int left_bn = get_button(0);
485
486         if(left_bn) {
487                 int dx = x - prev_x;
488                 int dy = y - prev_y;
489                 prev_x = x - dx;
490                 prev_y = y - dy;
491
492                 if(win->get_children()[0]->get_state() != Window::STATE_MAXIMIZED) {
493                         Rect rect = win->get_rect();
494                         win->move(rect.x + dx, rect.y + dy);
495                 }
496         }
497 }