1
2 /******************************************************************************
3 * MODULE : x_window.cpp
4 * DESCRIPTION: Windows under X11
5 * COPYRIGHT : (C) 1999 Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11
12 #include "X11/x_window.hpp"
13 #include "message.hpp"
14 #include "boot.hpp"
15 #include "x_picture.hpp"
16
17 extern int nr_windows;
18
19 hashmap<Window,pointer> Window_to_window (NULL);
20
21 /******************************************************************************
22 * Creation and deletion of an x_window
23 ******************************************************************************/
24
25 void
set_hints(int min_w,int min_h,int max_w,int max_h)26 x_window_rep::set_hints (int min_w, int min_h, int max_w, int max_h) {
27 XSizeHints* size_hints;
28 XWMHints* wm_hints;
29 XClassHint* class_hints;
30 ASSERT (size_hints= XAllocSizeHints (), "out of memory (X server)");
31 ASSERT (wm_hints= XAllocWMHints (), "out of memory (X server)");
32 ASSERT (class_hints= XAllocClassHint (), "out of memory (X server)");
33
34 XTextProperty Window_Name;
35 XTextProperty Icon_Name;
36 ASSERT (XStringListToTextProperty (&name, 1, &Window_Name) != 0,
37 "out of memory (X server)");
38 ASSERT (XStringListToTextProperty (&name, 1, &Icon_Name) != 0,
39 "out of memory (X server)");
40
41 // time_t start_1= texmacs_time ();
42 Pixmap pm= retrieve_pixmap (load_xpm ("TeXmacs.xpm"));
43 // cout << "Getting pixmap required " << (texmacs_time ()-start_1) << " ms\n";
44
45 // time_t start_2= texmacs_time ();
46 size_hints->flags = PPosition | PSize | PMinSize | PMaxSize;
47 size_hints->min_width = min_w;
48 size_hints->min_height = min_h;
49 size_hints->max_width = max_w;
50 size_hints->max_height = max_h;
51 wm_hints->initial_state = NormalState;
52 wm_hints->input = true;
53 wm_hints->icon_pixmap = pm;
54 wm_hints->flags = StateHint | IconPixmapHint | InputHint;
55 class_hints->res_name = name;
56 class_hints->res_class = name;
57
58 XSetWMProperties (
59 dpy,
60 win,
61 &Window_Name,
62 &Icon_Name,
63 gui->argv,
64 gui->argc,
65 size_hints,
66 wm_hints,
67 class_hints
68 );
69
70 XFree(size_hints);
71 XFree(wm_hints);
72 XFree(class_hints);
73 XFree(Window_Name.value);
74 XFree(Icon_Name.value);
75 // cout << "Setting hints required " << (texmacs_time ()-start_2) << " ms\n";
76 }
77
78 void
initialize()79 x_window_rep::initialize () {
80 SI min_w= Min_w, min_h= Min_h;
81 SI def_w= Def_w, def_h= Def_h;
82 SI max_w= Max_w, max_h= Max_h;
83
84 dpy= gui->dpy;
85 gc = gui->gc;
86 full_screen_flag= false;
87
88 // time_t start_1= texmacs_time ();
89 ren->set_origin (0, 0);
90 ren->decode (def_w, def_h); def_h= -def_h;
91 ren->decode (min_w, min_h); min_h= -min_h;
92 ren->decode (max_w, max_h); max_h= -max_h;
93 // cout << "Size computation required " << (texmacs_time ()-start_1) << " ms\n";
94
95 // time_t start_2= texmacs_time ();
96 unsigned long valuemask= CWOverrideRedirect | CWSaveUnder;
97 //unsigned long valuemask= CWOverrideRedirect | CWSaveUnder | CWBackingStore;
98 XSetWindowAttributes setattr;
99 setattr.override_redirect= (name==NULL);
100 setattr.save_under = True; // (name==NULL);
101 // setattr.backing_store = Always;
102 // FIXME: backing store does not seem to work correctly
103 if (win_w == 0) win_w= def_w;
104 if (win_h == 0) win_h= def_h;
105 if ((win_x+ win_w) > gui->screen_width) win_x= gui->screen_width- win_w;
106 if (win_x < 0) win_x= 0;
107 if ((win_y+ win_h) > gui->screen_height) win_y= gui->screen_height- win_h;
108 if (win_y < 0) win_y=0;
109 win= XCreateWindow (dpy, gui->root, win_x, win_y, win_w, win_h, 0,
110 gui->depth, InputOutput, CopyFromParent,
111 valuemask, &setattr);
112 ren->win= (Drawable) win;
113 // cout << "XWindow creation required " << (texmacs_time ()-start_2) << " ms\n";
114
115 //cout << "Hints: "
116 // << min_w << ", " << min_h << " --- "
117 // << def_w << ", " << def_h << " --- "
118 // << max_w << ", " << max_h << "\n";
119 if (name == NULL) name= const_cast<char*> ("popup");
120 if (the_name == "") {
121 the_name= name;
122 mod_name= name;
123 }
124 set_hints (min_w, min_h, max_w, max_h);
125
126 unsigned long ic_mask= 0;
127 ic_ok= false;
128 if (gui->im_ok) {
129 ic= XCreateIC (gui->im,
130 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
131 XNClientWindow, win,
132 NULL);
133 if (ic == NULL)
134 cout << "TeXmacs] Warning: couldn't create input context\n";
135 else {
136 ic_ok= true;
137 XGetICValues (ic, XNFilterEvents, &ic_mask, NULL);
138 }
139 }
140
141 XSelectInput (dpy, win,
142 ExposureMask | StructureNotifyMask |
143 SubstructureNotifyMask | FocusChangeMask |
144 PointerMotionMask | EnterWindowMask | LeaveWindowMask |
145 ButtonPressMask | ButtonReleaseMask |
146 KeyPressMask | ic_mask);
147
148 Atom wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", 1);
149 Atom wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", 1);
150 XSetWMProtocols (dpy, win, &wm_protocols, 1);
151 XSetWMProtocols (dpy, win, &wm_delete_window, 1);
152
153 nr_windows++;
154 Window_to_window (win)= (void*) this;
155 set_identifier (w, (int) win);
156 notify_position (w, 0, 0);
157 notify_size (w, Def_w, Def_h);
158 }
159
160 static x_drawable_rep*
new_drawable(x_gui gui,x_window win)161 new_drawable (x_gui gui, x_window win) {
162 // FIXME: for gcc 3.*, use this in order to avoid ambiguity
163 // for tm_new with two arguments
164 return new x_drawable_rep (gui, win);
165 //return tm_new<x_drawable_rep> (gui, win);
166 }
167
x_window_rep(widget w2,x_gui gui2,char * n2,SI min_w,SI min_h,SI def_w,SI def_h,SI max_w,SI max_h)168 x_window_rep::x_window_rep (widget w2, x_gui gui2, char* n2,
169 SI min_w, SI min_h, SI def_w, SI def_h,
170 SI max_w, SI max_h):
171 window_rep (), w (w2), gui (gui2),
172 orig_name (n2 == ((char*) NULL)? string ("popup"): n2), name (n2),
173 ren (new_drawable (gui2, this)),
174 Min_w (min_w), Min_h (min_h), Def_w (def_w), Def_h (def_h),
175 Max_w (max_w), Max_h (max_h),
176 win_x (0), win_y (0), win_w (Def_w/PIXEL), win_h (Def_h/PIXEL),
177 kbd_focus (w.rep), has_focus (false)
178 {
179 //cout << "Min " << (min_w >> 8) << ", " << (min_h >> 8) << "\n";
180 //cout << "Def " << (def_w >> 8) << ", " << (def_h >> 8) << "\n";
181 //cout << "Max " << (max_w >> 8) << ", " << (max_h >> 8) << "\n";
182
183 initialize ();
184 gui->created_window (win);
185 }
186
~x_window_rep()187 x_window_rep::~x_window_rep () {
188 set_identifier (w, 0);
189
190 XEvent report;
191 while (XCheckWindowEvent (dpy, win, 0xffffffff, &report));
192
193 tm_delete (ren);
194 if (ic_ok) XDestroyIC (ic);
195 Window_to_window->reset (win);
196 nr_windows--;
197 XDestroyWindow (dpy, win);
198 gui->deleted_window (win);
199 }
200
201 widget
get_widget()202 x_window_rep::get_widget () {
203 return w;
204 }
205
206 void
get_extents(int & w,int & h)207 x_window_rep::get_extents (int& w, int& h) {
208 w= win_w;
209 h= win_h;
210 }
211
212 Window
get_Window(widget w)213 get_Window (widget w) {
214 int id= get_identifier (w);
215 if (id == 0) {
216 failed_error << "widget = " << w << "\n";
217 FAILED ("widget is not attached to a window");
218 }
219 return (Window) id;
220 }
221
222 x_window
get_x_window(widget w)223 get_x_window (widget w) {
224 int id= get_identifier (w);
225 if (id == 0) return NULL;
226 else return (x_window) Window_to_window[(Window) id];
227 }
228
229 int
get_identifier(window w)230 get_identifier (window w) {
231 if (w == NULL) return 0;
232 else return (int) (((x_window) w) -> win);
233 }
234
235 window
get_window(int id)236 get_window (int id) {
237 if (id == 0) return NULL;
238 else return (window) ((x_window) Window_to_window[(Window) id]);
239 }
240
241 /******************************************************************************
242 * Window apping and appearance
243 ******************************************************************************/
244
245 void
get_position(SI & x,SI & y)246 x_window_rep::get_position (SI& x, SI& y) {
247 #ifdef OS_WIN32
248 XGetWindowPos (dpy, win, &win_x, &win_y);
249 x= win_x*PIXEL;
250 y= -win_y*PIXEL;
251 #else
252 int xx, yy;
253 Window ww;
254 XTranslateCoordinates (dpy, win, gui->root, 0, 0, &xx, &yy, &ww);
255 x= xx*PIXEL;
256 y= -yy*PIXEL;
257 #endif
258 }
259
260 void
get_size(SI & ww,SI & hh)261 x_window_rep::get_size (SI& ww, SI& hh) {
262 ww= win_w*PIXEL;
263 hh= win_h*PIXEL;
264 }
265
266 void
get_size_limits(SI & min_w,SI & min_h,SI & max_w,SI & max_h)267 x_window_rep::get_size_limits (SI& min_w, SI& min_h, SI& max_w, SI& max_h) {
268 min_w= Min_w; min_h= Min_h; max_w= Max_w; max_h= Max_h;
269 }
270
271 void
set_position(SI x,SI y)272 x_window_rep::set_position (SI x, SI y) {
273 x= x/PIXEL;
274 y= -y/PIXEL;
275 if ((x+ win_w) > gui->screen_width) x= gui->screen_width- win_w;
276 if (x<0) x=0;
277 if ((y+ win_h) > gui->screen_height) y= gui->screen_height- win_h;
278 if (y<0) y=0;
279 XMoveWindow (dpy, win, x, y);
280 }
281
282 void
set_size(SI w,SI h)283 x_window_rep::set_size (SI w, SI h) {
284 w= w/PIXEL; h= h/PIXEL;
285 //h=-h; ren->decode (w, h);
286 XResizeWindow (dpy, win, w, h);
287 }
288
289 void
set_size_limits(SI min_w,SI min_h,SI max_w,SI max_h)290 x_window_rep::set_size_limits (SI min_w, SI min_h, SI max_w, SI max_h) {
291 if (min_w == Min_w && min_h == Min_h && max_w == Max_w && max_h == Max_h)
292 return;
293 Min_w= min_w; Min_h= min_h; Max_w= max_w; Max_h= max_h;
294 min_w= min_w/PIXEL; min_h= min_h/PIXEL;
295 max_w= max_w/PIXEL; max_h= max_h/PIXEL;
296
297 XSizeHints* size_hints;
298 ASSERT (size_hints= XAllocSizeHints (), "out of memory (X server)");
299 size_hints->flags = PMinSize | PMaxSize;
300 size_hints->min_width = min_w;
301 size_hints->min_height = min_h;
302 size_hints->max_width = max_w;
303 size_hints->max_height = max_h;
304 XSetWMNormalHints (dpy, win, size_hints);
305 XFree(size_hints);
306 }
307
308 void
set_name(string name)309 x_window_rep::set_name (string name) {
310 if (the_name != name) {
311 c_string s (name);
312 XStoreName (dpy, win, s);
313 XSetIconName (dpy, win, s);
314 the_name= name;
315 mod_name= name;
316 }
317 }
318
319 string
get_name()320 x_window_rep::get_name () {
321 return the_name;
322 }
323
324 void
set_modified(bool flag)325 x_window_rep::set_modified (bool flag) {
326 string name= (flag? (the_name * " *"): the_name);
327 if (mod_name != name) {
328 c_string s (name);
329 XStoreName (dpy, win, s);
330 XSetIconName (dpy, win, s);
331 mod_name= name;
332 }
333 }
334
335 void
set_visibility(bool flag)336 x_window_rep::set_visibility (bool flag) {
337 if (flag) XMapRaised (dpy, win);
338 else XUnmapWindow (dpy, win);
339 }
340
341 void
set_full_screen(bool flag)342 x_window_rep::set_full_screen (bool flag) {
343 if (full_screen_flag == flag) return;
344 string old_name= get_name ();
345 if (old_name == "")
346 old_name= as_string (name);
347 if (flag) {
348 save_win= win;
349 name= NULL;
350 save_x= win_x; save_y= win_y;
351 save_w= win_w; save_h= win_h;
352 initialize ();
353 XMoveResizeWindow (dpy, win, 0, 0,
354 gui->screen_width, gui->screen_height);
355 move_event (0, 0);
356 resize_event (gui->screen_width, gui->screen_height);
357 set_visibility (true);
358 XSetInputFocus (dpy, win, PointerRoot, CurrentTime);
359 }
360 else {
361 set_visibility (false);
362 Window_to_window->reset (win);
363 nr_windows--;
364 XDestroyWindow (dpy, win);
365 win= save_win;
366 set_visibility (false);
367 Window_to_window->reset (win);
368 nr_windows--;
369 XDestroyWindow (dpy, win);
370 //FIXME: is this 'as_charp' a possible memory leak?
371 name= as_charp (old_name);
372 win_x= save_x; win_y= save_y;
373 win_w= save_w; win_h= save_h;
374 initialize ();
375 set_visibility (true);
376 XMoveResizeWindow (dpy, win, save_x, save_y, save_w, save_h);
377 resize_event (save_w, save_h);
378 move_event (save_x, save_y);
379 }
380 set_name (old_name);
381 full_screen_flag= flag;
382 }
383
384 void
move_event(int x,int y)385 x_window_rep::move_event (int x, int y) {
386 bool flag= (win_x!=x) || (win_y!=y);
387 win_x= x; win_y= y;
388 if (flag) {
389 XWindowAttributes attrs;
390 XGetWindowAttributes (dpy, win, &attrs);
391 int border_x= attrs.x, border_y= attrs.y;
392 notify_position (w, win_x*PIXEL, win_y*PIXEL);
393 if (border_x >= 0 && border_x <= 5 && border_y >= 0 && border_y <= 30) {
394 //cout << "Move to " << x-border_x << ", " << y-border_y << "\n";
395 notify_window_move (orig_name, (x-border_x)*PIXEL, (border_y-y)*PIXEL);
396 }
397 }
398 }
399
400 void
resize_event(int ww,int hh)401 x_window_rep::resize_event (int ww, int hh) {
402 bool flag= (win_w!=ww) || (win_h!=hh);
403 win_w= ww; win_h= hh;
404 if (flag) {
405 notify_size (w, win_w*PIXEL, win_h*PIXEL);
406 notify_window_resize (orig_name, ww*PIXEL, hh*PIXEL);
407 }
408 }
409
410 void
destroy_event()411 x_window_rep::destroy_event () {
412 notify_window_destroy (orig_name);
413 send_destroy (w);
414 }
415
416 /******************************************************************************
417 * Event handling
418 ******************************************************************************/
419
420 void
invalidate_event(int x1,int y1,int x2,int y2)421 x_window_rep::invalidate_event (int x1, int y1, int x2, int y2) {
422 invalid_regions= invalid_regions | rectangles (rectangle (x1, y1, x2, y2));
423 }
424
425 void
key_event(string key)426 x_window_rep::key_event (string key) {
427 send_keyboard (kbd_focus, key);
428 }
429
430 void
focus_in_event()431 x_window_rep::focus_in_event () {
432 if (ic_ok) XSetICFocus (ic);
433 has_focus= true;
434 notify_keyboard_focus (kbd_focus, true);
435 gui->focussed_window (win);
436 }
437
438 void
focus_out_event()439 x_window_rep::focus_out_event () {
440 if (ic_ok) XUnsetICFocus (ic);
441 has_focus= false;
442 notify_keyboard_focus (kbd_focus, false);
443 }
444
445 void
mouse_event(string ev,int x,int y,time_t t)446 x_window_rep::mouse_event (string ev, int x, int y, time_t t) {
447 if (is_nil (gui->grab_ptr) || (get_x_window (gui->grab_ptr->item) == NULL)) {
448 ren->set_origin (0, 0);
449 ren->encode (x, y);
450 send_mouse (w, ev, x, y, gui->state, t);
451 }
452 else {
453 x_window grab_win= get_x_window (gui->grab_ptr->item);
454 if (this != grab_win) {
455 x += win_x - grab_win->win_x;
456 y += win_y - grab_win->win_y;
457 // return;
458 }
459 ren->set_origin (0, 0);
460 ren->encode (x, y);
461 send_mouse (gui->grab_ptr->item, ev, x, y, gui->state, t);
462 }
463 }
464
465 void
repaint_invalid_regions()466 x_window_rep::repaint_invalid_regions () {
467 //if (!is_nil (invalid_regions)) cout << invalid_regions << "\n";
468 //else { cout << "."; cout.flush (); }
469 rectangles new_regions;
470 if (!is_nil (invalid_regions)) {
471 rectangle lub= least_upper_bound (invalid_regions);
472 if (area (lub) < 1.2 * area (invalid_regions))
473 invalid_regions= rectangles (lub);
474 }
475 while (!is_nil (invalid_regions)) {
476 ren->set_origin (0, 0);
477 rectangle r= copy (invalid_regions->item);
478 r= thicken (r, 1, 1);
479 ren->encode (r->x1, r->y1);
480 ren->encode (r->x2, r->y2);
481 ren->set_clipping (r->x1, r->y2, r->x2, r->y1);
482 send_repaint (w, ren, r->x1, r->y2, r->x2, r->y1);
483 if (gui_interrupted ())
484 new_regions= rectangles (invalid_regions->item, new_regions);
485 invalid_regions= invalid_regions->next;
486 }
487 invalid_regions= new_regions;
488 }
489
490 void
set_keyboard_focus(widget wid,bool get_focus)491 x_window_rep::set_keyboard_focus (widget wid, bool get_focus) {
492 ASSERT (get_focus, "explicit loss of keyboard focus not yet implemented");
493 if (has_focus && (kbd_focus != wid.rep)) {
494 notify_keyboard_focus (kbd_focus, false);
495 notify_keyboard_focus (wid, true);
496 }
497 kbd_focus= wid.rep;
498 }
499
500 bool
get_keyboard_focus(widget wid)501 x_window_rep::get_keyboard_focus (widget wid) {
502 return has_focus && kbd_focus == wid.rep;
503 }
504
505 void
set_mouse_grab(widget wid,bool get_grab)506 x_window_rep::set_mouse_grab (widget wid, bool get_grab) {
507 if (get_grab) gui->obtain_mouse_grab (wid);
508 else gui->release_mouse_grab ();
509 }
510
511 bool
get_mouse_grab(widget w)512 x_window_rep::get_mouse_grab (widget w) {
513 return gui->has_mouse_grab (w);
514 }
515
516 void
set_mouse_pointer(widget wid,string name,string mask)517 x_window_rep::set_mouse_pointer (widget wid, string name, string mask) {
518 if (mask == "") gui->set_mouse_pointer (wid, name);
519 else gui->set_mouse_pointer (wid, name, mask);
520 }
521
522 /******************************************************************************
523 * Delayed messages
524 ******************************************************************************/
525
message_rep(widget wid2,string s2,time_t t2)526 message_rep::message_rep (widget wid2, string s2, time_t t2):
527 wid (wid2), s (s2), t (t2) {}
message(widget wid,string s,time_t t)528 message::message (widget wid, string s, time_t t):
529 rep (tm_new<message_rep> (wid, s, t)) {}
530
531 tm_ostream&
operator <<(tm_ostream & out,message m)532 operator << (tm_ostream& out, message m) {
533 return out << "message " << m->s << " to " << m->wid
534 << "at time " << m->t << "\n";
535 }
536
537 static list<message>
insert_message(list<message> l,widget wid,string s,time_t cur,time_t t)538 insert_message (list<message> l, widget wid, string s, time_t cur, time_t t) {
539 if (is_nil (l)) return list<message> (message (wid, s, t));
540 time_t ref= l->item->t;
541 if ((t-cur) <= (ref-cur)) return list<message> (message (wid, s, t), l);
542 return list<message> (l->item, insert_message (l->next, wid, s, cur, t));
543 }
544
545 void
delayed_message(widget wid,string s,time_t delay)546 x_window_rep::delayed_message (widget wid, string s, time_t delay) {
547 time_t ct= texmacs_time ();
548 the_gui->messages= insert_message (the_gui->messages, wid, s, ct, ct+ delay);
549 }
550
551 /******************************************************************************
552 * Routines concerning regions in a window
553 ******************************************************************************/
554
555 void
translate(SI x1,SI y1,SI x2,SI y2,SI dx,SI dy)556 x_window_rep::translate (SI x1, SI y1, SI x2, SI y2, SI dx, SI dy) {
557 ren->set_origin(0,0);
558 begin_draw ();
559 ren->clip (x1, y1, x2, y2);
560
561 SI X1= x1+ dx;
562 SI Y2= y2+ dy;
563 ren->decode (x1, y1);
564 ren->decode (x2, y2);
565 ren->decode (X1, Y2);
566 dx= X1- x1;
567 dy= Y2- y2;
568
569 XEvent report;
570 while (XCheckWindowEvent (dpy, win, ExposureMask, &report))
571 gui->process_event (this, &report);
572
573 rectangles region (rectangle (x1, y2, x2, y1));
574 rectangles invalid_intern= invalid_regions & region;
575 rectangles invalid_extern= invalid_regions - invalid_intern;
576 invalid_intern = ::translate (invalid_intern, dx, dy) & region;
577 invalid_regions= invalid_extern | invalid_intern;
578
579 rectangles extra= thicken (region - ::translate (region, dx, dy), 1, 1);
580 invalid_regions= invalid_regions | extra;
581
582 if (x1<x2 && y2<y1)
583 XCopyArea (dpy, win, win, gc, x1, y2, x2-x1, y1-y2, X1, Y2);
584
585
586 ren->unclip ();
587 end_draw ();
588 }
589
590 void
invalidate(SI x1,SI y1,SI x2,SI y2)591 x_window_rep::invalidate (SI x1, SI y1, SI x2, SI y2) {
592 ren->outer_round (x1, y1, x2, y2);
593 ren->decode (x1, y1);
594 ren->decode (x2, y2);
595 invalidate_event (x1, y2, x2, y1);
596 }
597
598 bool
is_invalid()599 x_window_rep::is_invalid () {
600 return ! is_nil (invalid_regions);
601 }
602
603 /******************************************************************************
604 * Interface
605 ******************************************************************************/
606
607 window
popup_window(widget w,string name,SI min_w,SI min_h,SI def_w,SI def_h,SI max_w,SI max_h)608 popup_window (widget w, string name, SI min_w, SI min_h,
609 SI def_w, SI def_h, SI max_w, SI max_h)
610 {
611 window win= tm_new<x_window_rep> (w, the_gui, (char*) NULL,
612 min_w, min_h, def_w, def_h, max_w, max_h);
613 return win;
614 }
615
616 window
plain_window(widget w,string name,SI min_w,SI min_h,SI def_w,SI def_h,SI max_w,SI max_h)617 plain_window (widget w, string name, SI min_w, SI min_h,
618 SI def_w, SI def_h, SI max_w, SI max_h)
619 {
620 c_string _name (name);
621 window win= tm_new<x_window_rep> (w, the_gui, _name,
622 min_w, min_h, def_w, def_h, max_w, max_h);
623 return win;
624 }
625