2009-10-20 Chris Toshok <toshok@ximian.com>
[moon.git] / src / window-gtk.cpp
blob913b66c123906e756c99e53aa5869569c883668c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * window-gtk.cpp: MoonWindow implementation using gtk widgets.
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include "window-gtk.h"
15 #include "deployment.h"
16 #include "timemanager.h"
18 MoonWindowGtk::MoonWindowGtk (bool fullscreen, int w, int h, MoonWindow *parent)
19 : MoonWindow (w, h)
21 this->fullscreen = fullscreen;
23 if (fullscreen)
24 InitializeFullScreen(parent);
25 else
26 InitializeNormal();
29 MoonWindowGtk::~MoonWindowGtk ()
31 /* gtk_widget_destroy can cause reentry (into another plugin if this destruction causes layout changes) */
32 DeploymentStack deployment_push_pop;
33 DisableEvents ();
34 if (widget != NULL)
35 gtk_widget_destroy (widget);
38 GdkWindow *
39 MoonWindowGtk::GetGdkWindow ()
41 GdkWindow *parent_window = gtk_widget_get_parent_window (widget);
42 if (parent_window == NULL)
43 parent_window = widget->window;
45 g_object_ref (parent_window);
46 return parent_window;
49 void
50 MoonWindowGtk::InitializeFullScreen (MoonWindow *parent)
52 widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
54 // only fullscreen on the monitor the plugin is on
55 GdkWindow *gdk = parent->GetGdkWindow ();
56 int monitor = gdk_screen_get_monitor_at_window (gdk_screen_get_default (), gdk);
57 GdkRectangle bounds;
58 gdk_screen_get_monitor_geometry (gdk_screen_get_default (), monitor, &bounds);
59 width = bounds.width;
60 height = bounds.height;
61 gtk_window_move (GTK_WINDOW (widget), bounds.x, bounds.y);
63 gtk_window_fullscreen (GTK_WINDOW (widget));
65 InitializeCommon ();
67 Show();
69 g_object_unref (gdk);
72 void
73 MoonWindowGtk::InitializeNormal ()
75 if (width == -1 || height == -1) {
76 g_warning ("you must specify width and height when creating a non-fullscreen gtk window");
77 width = 0;
78 height = 0;
81 widget = gtk_event_box_new ();
83 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), false);
85 InitializeCommon ();
87 Show ();
90 void
91 MoonWindowGtk::InitializeCommon ()
93 // don't let gtk clear the window we'll do all the drawing.
94 //gtk_widget_set_app_paintable (widget, true);
95 gtk_widget_set_double_buffered (widget, false);
96 gtk_widget_set_size_request (widget, width, height);
98 g_signal_connect (widget, "size-allocate", G_CALLBACK (widget_size_allocate), this);
99 g_signal_connect (widget, "destroy", G_CALLBACK (widget_destroyed), this);
101 gtk_widget_add_events (widget,
102 GDK_POINTER_MOTION_MASK |
103 //GDK_POINTER_MOTION_HINT_MASK |
104 GDK_KEY_PRESS_MASK |
105 GDK_KEY_RELEASE_MASK |
106 GDK_BUTTON_PRESS_MASK |
107 GDK_BUTTON_RELEASE_MASK |
108 ((moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) != 0 ? GDK_SCROLL_MASK : 0) |
109 GDK_FOCUS_CHANGE_MASK);
111 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
114 void
115 MoonWindowGtk::Resize (int width, int height)
117 gtk_widget_set_size_request (widget, width, height);
118 gtk_widget_queue_resize (widget);
121 /* XPM */
122 static const char *dot[] = {
123 "18 18 4 1",
124 " c None",
125 ". c #808080",
126 "+ c #303030",
127 "@ c #000000",
128 ".+. ",
129 "@@@ ",
130 ".@. ",
131 " ",
132 " ",
133 " ",
134 " ",
135 " ",
136 " ",
137 " ",
138 " ",
139 " ",
140 " ",
141 " ",
142 " ",
143 " ",
144 " ",
148 /* XPM */
149 static const char *eraser[] = {
150 "20 20 49 1",
151 " c None",
152 ". c #000000",
153 "+ c #858585",
154 "@ c #E8E8E8",
155 "# c #E9E9E9",
156 "$ c #E7E7E7",
157 "% c #E2E2E2",
158 "& c #D6D6D6",
159 "* c #7D7D7D",
160 "= c #565656",
161 "- c #E1E1E1",
162 "; c #E0E0E0",
163 "> c #DEDEDE",
164 ", c #DFDFDF",
165 "' c #474747",
166 ") c #6C6C6C",
167 "! c #B0B0B0",
168 "~ c #E3E3E3",
169 "{ c #4E4E4E",
170 "] c #636363",
171 "^ c #E6E6E6",
172 "/ c #505050",
173 "( c #4A4A4A",
174 "_ c #C7C7C7",
175 ": c #272727",
176 "< c #797979",
177 "[ c #E5E5E5",
178 "} c #DDDDDD",
179 "| c #9C9C9C",
180 "1 c #232323",
181 "2 c #E4E4E4",
182 "3 c #656565",
183 "4 c #313131",
184 "5 c #EAEAEA",
185 "6 c #ECECEC",
186 "7 c #EEEEEE",
187 "8 c #EFEFEF",
188 "9 c #F0F0F0",
189 "0 c #999999",
190 "a c #5D5D5D",
191 "b c #343434",
192 "c c #757575",
193 "d c #383838",
194 "e c #CECECE",
195 "f c #A9A9A9",
196 "g c #6F6F6F",
197 "h c #B3B3B3",
198 "i c #787878",
199 "j c #3F3F3F",
200 " ",
201 " ",
202 " ",
203 " ",
204 " ",
205 " ",
206 " ........... ",
207 " .+@#@@@$$%&*. ",
208 " =-;%>>>>>>>,' ",
209 " )!~>>>>>>>>>>{ ",
210 " ]^>>>>>>>>>,,/ ",
211 " (_;>>>>>>>>,>&: ",
212 " <[,}>>>>>>>-,| ",
213 " 1[-;>>>>>>>$2,3 ",
214 " 45678999998550a ",
215 " b~,,,,,,,,,;$c ",
216 " de-,,,,,,,,,fg ",
217 " bh%%,,;}}}>>ij ",
218 " ............ ",
222 void
223 MoonWindowGtk::SetBackgroundColor (Color *color)
225 GdkColor gdk_color;
226 gdk_color.red = color->r * 0xffff;
227 gdk_color.green = color->g * 0xffff;
228 gdk_color.blue = color->b * 0xffff;
230 gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, &gdk_color);
232 MoonWindow::SetBackgroundColor (color);
235 void
236 MoonWindowGtk::SetCursor (MouseCursor cursor)
238 if (widget->window) {
240 GdkCursor *c = NULL;
241 switch (cursor) {
242 case MouseCursorDefault:
243 c = NULL;
244 break;
245 case MouseCursorArrow:
246 c = gdk_cursor_new (GDK_LEFT_PTR);
247 break;
248 case MouseCursorHand:
249 c = gdk_cursor_new (GDK_HAND2);
250 break;
251 case MouseCursorWait:
252 c = gdk_cursor_new (GDK_WATCH);
253 break;
254 case MouseCursorIBeam:
255 c = gdk_cursor_new (GDK_XTERM);
256 break;
257 case MouseCursorStylus:
258 c = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), gdk_pixbuf_new_from_xpm_data ((const char**) dot), 0, 0);
259 break;
260 case MouseCursorEraser:
261 c = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), gdk_pixbuf_new_from_xpm_data ((const char**) eraser), 8, 8);
262 break;
263 case MouseCursorSizeNS:
264 c = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
265 break;
266 case MouseCursorSizeWE:
267 c = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
268 break;
269 case MouseCursorNone:
270 // Silverlight display no cursor if the enumeration value is invalid (e.g. -1)
271 default:
272 //from gdk-cursor doc :"To make the cursor invisible, use gdk_cursor_new_from_pixmap() to create a cursor with no pixels in it."
273 GdkPixmap *empty = gdk_bitmap_create_from_data (NULL, "0x00", 1, 1);
274 GdkColor empty_color = {0, 0, 0, 0};
275 c = gdk_cursor_new_from_pixmap (empty, empty, &empty_color, &empty_color, 0, 0);
276 g_object_unref (empty);
277 break;
281 gdk_window_set_cursor (widget->window, c);
283 if (c)
284 gdk_cursor_unref (c);
288 void
289 MoonWindowGtk::Invalidate (Rect r)
291 gtk_widget_queue_draw_area (widget,
292 (int) (widget->allocation.x + r.x),
293 (int) (widget->allocation.y + r.y),
294 (int) r.width, (int)r.height);
297 void
298 MoonWindowGtk::ProcessUpdates ()
300 if (widget->window)
301 gdk_window_process_updates (widget->window, false);
304 gboolean
305 MoonWindowGtk::HandleEvent (XEvent *event)
307 // nothing to do here, since we don't pump events into the gtk
308 // window, gtk calls our signal handlers directly.
309 return TRUE;
312 void
313 MoonWindowGtk::Show ()
315 gtk_widget_show (widget);
317 // The window has to be realized for this call to work
318 gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_CURSOR);
319 /* we need to explicitly enable the devices */
320 for (GList *l = gdk_devices_list(); l; l = l->next) {
321 #if THIS_NOLONGER_BREAKS_LARRYS_MOUSE
322 GdkDevice *device = GDK_DEVICE(l->data);
323 //if (!device->has_cursor)
324 gdk_device_set_mode (device, GDK_MODE_SCREEN);
325 #endif
328 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
331 void
332 MoonWindowGtk::Hide ()
334 gtk_widget_hide (widget);
337 void
338 MoonWindowGtk::EnableEvents (bool first)
340 g_signal_connect (widget, "expose-event", G_CALLBACK (expose_event), this);
341 g_signal_connect (widget, "motion-notify-event", G_CALLBACK (motion_notify), this);
342 g_signal_connect (widget, "enter-notify-event", G_CALLBACK (crossing_notify), this);
343 g_signal_connect (widget, "leave-notify-event", G_CALLBACK (crossing_notify), this);
344 g_signal_connect (widget, "key-press-event", G_CALLBACK (key_press), this);
345 g_signal_connect (widget, "key-release-event", G_CALLBACK (key_release), this);
346 g_signal_connect (widget, "button-press-event", G_CALLBACK (button_press), this);
347 g_signal_connect (widget, "button-release-event", G_CALLBACK (button_release), this);
348 g_signal_connect (widget, "scroll-event", G_CALLBACK (scroll), this);
349 g_signal_connect (widget, "focus-in-event", G_CALLBACK (focus_in), this);
350 g_signal_connect (widget, "focus-out-event", G_CALLBACK (focus_out), this);
352 if (first) {
353 g_signal_connect (widget, "realize", G_CALLBACK (realized), this);
354 g_signal_connect (widget, "unrealize", G_CALLBACK (unrealized), this);
356 if (GTK_WIDGET_REALIZED (widget))
357 realized (widget, this);
361 void
362 MoonWindowGtk::DisableEvents ()
364 g_signal_handlers_disconnect_matched (widget, G_SIGNAL_MATCH_DATA,
365 0, 0, NULL, NULL, this);
368 void
369 MoonWindowGtk::GrabFocus ()
371 gtk_widget_grab_focus (widget);
374 bool
375 MoonWindowGtk::HasFocus ()
377 return GTK_WIDGET_HAS_FOCUS (widget);
380 gboolean
381 MoonWindowGtk::expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data)
383 MoonWindowGtk *window = (MoonWindowGtk*)data;
385 window->SetCurrentDeployment ();
387 if (!window->surface)
388 return true;
390 // we draw to a backbuffer pixmap, then transfer the contents
391 // to the widget's window.
392 GdkPixmap *pixmap = gdk_pixmap_new (widget->window,
393 MAX (event->area.width, 1), MAX (event->area.height, 1), -1);
395 window->surface->PaintToDrawable (pixmap,
396 gdk_drawable_get_visual (widget->window),
397 event,
398 widget->allocation.x,
399 widget->allocation.y,
400 window->GetTransparent (),
401 true);
403 GdkGC *gc = gdk_gc_new (pixmap);
405 gdk_gc_set_clip_region (gc, event->region);
407 gdk_draw_drawable (widget->window, gc, pixmap,
408 0, 0,
409 event->area.x, event->area.y,
410 event->area.width, event->area.height);
412 g_object_unref (pixmap);
413 g_object_unref (gc);
415 return true;
418 gboolean
419 MoonWindowGtk::button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
421 MoonWindowGtk *window = (MoonWindowGtk*)data;
423 window->SetCurrentDeployment ();
425 if (event->button != 1 && event->button != 3)
426 return false;
428 if (window->surface)
429 window->surface->HandleUIButtonPress (event);
431 // If we don't support right clicks (i.e. inside the browser)
432 // return false here
433 if (event->button == 3 && (moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) == 0)
434 return false;
436 // ignore HandleUIButtonPress's return value, and always
437 // return true here, or it gets bubbled up to firefox.
438 return true;
441 gboolean
442 MoonWindowGtk::button_release (GtkWidget *widget, GdkEventButton *event, gpointer data)
444 MoonWindowGtk *window = (MoonWindowGtk*)data;
446 window->SetCurrentDeployment ();
448 if (window->surface)
449 window->surface->HandleUIButtonRelease (event);
450 // ignore HandleUIButtonRelease's return value, and always
451 // return true here, or it gets bubbled up to firefox.
452 return true;
455 gboolean
456 MoonWindowGtk::scroll (GtkWidget *widget, GdkEventScroll *event, gpointer data)
458 MoonWindowGtk *window = (MoonWindowGtk*)data;
460 window->SetCurrentDeployment ();
462 if (window->surface)
463 window->surface->HandleUIScroll (event);
464 // ignore HandleUIScroll's return value, and always
465 // return true here, or it gets bubbled up to firefox.
466 return true;
469 gboolean
470 MoonWindowGtk::motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
472 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
474 window->SetCurrentDeployment ();
476 if (window->surface)
477 window->surface->HandleUIMotion (event);
478 // ignore HandleUIMotion's return value, and always
479 // return true here, or it gets bubbled up to firefox.
480 return true;
483 gboolean
484 MoonWindowGtk::crossing_notify (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
486 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
488 window->SetCurrentDeployment ();
490 if (window->surface) {
491 window->surface->HandleUICrossing (event);
492 return true;
495 return false;
498 gboolean
499 MoonWindowGtk::focus_in (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
501 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
503 window->SetCurrentDeployment ();
505 if (window->surface) {
506 window->surface->HandleUIFocusIn (event);
507 return true;
510 return false;
513 gboolean
514 MoonWindowGtk::focus_out (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
516 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
518 window->SetCurrentDeployment ();
520 if (window->surface) {
521 window->surface->HandleUIFocusOut (event);
522 return true;
525 return false;
528 gboolean
529 MoonWindowGtk::key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
531 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
533 window->SetCurrentDeployment ();
535 if (window->surface) {
536 window->surface->HandleUIKeyPress (event);
537 return true;
540 return false;
543 gboolean
544 MoonWindowGtk::key_release (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
546 MoonWindowGtk *window = (MoonWindowGtk*)user_data;
548 window->SetCurrentDeployment ();
550 if (window->surface) {
551 window->surface->HandleUIKeyRelease (event);
552 return true;
555 return false;
558 void
559 MoonWindowGtk::widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer data)
561 MoonWindowGtk *window = (MoonWindowGtk*)data;
563 window->SetCurrentDeployment ();
565 //printf ("Surface::size-allocate callback: current = %dx%d; new = %dx%d\n",
566 // s->width, s->height, allocation->width, allocation->height);
568 bool emit_resize = false;
570 if (window->width != allocation->width || window->height != allocation->height) {
571 window->width = allocation->width;
572 window->height = allocation->height;
574 emit_resize = true;
577 if (window->surface)
578 window->surface->HandleUIWindowAllocation (emit_resize);
581 void
582 MoonWindowGtk::widget_destroyed (GtkWidget *widget, gpointer user_data)
584 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
586 window->widget = NULL;
587 if (window->surface)
588 window->surface->HandleUIWindowDestroyed (window);
591 gboolean
592 MoonWindowGtk::realized (GtkWidget *widget, gpointer user_data)
594 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
596 #ifdef USE_XRANDR
597 #if INTEL_DRIVERS_STOP_SUCKING
598 // apparently the i965 drivers blank external screens when
599 // getting the screen info (um, ugh?). needless to say, this
600 // annoyance is worse than not using the monitor's refresh as
601 // the upper bound for our fps.
603 // http://lists.freedesktop.org/archives/xorg/2007-August/027616.html
604 int event_base, error_base;
605 GdkWindow *gdk_root = gtk_widget_get_root_window (widget);
606 Display *dpy = GDK_WINDOW_XDISPLAY(gdk_root);
607 Window root = GDK_WINDOW_XID (gdk_root);
608 if (XRRQueryExtension (dpy, &event_base, &error_base)) {
609 XRRScreenConfiguration *info = XRRGetScreenInfo (dpy,
610 root);
611 short rate = XRRConfigCurrentRate (info);
612 printf ("screen refresh rate = %d\n", rate);
613 if (window->surface)
614 window->surface->GetTimeManager()->SetMaximumRefreshRate (rate);
615 XRRFreeScreenConfigInfo (info);
617 #endif
618 #endif
620 window->SetCurrentDeployment ();
622 if (window->surface) {
623 window->surface->HandleUIWindowUnavailable ();
624 window->surface->HandleUIWindowAvailable ();
627 #if SANITY
628 Deployment::SetCurrent (NULL);
629 #endif
631 return true;
634 gboolean
635 MoonWindowGtk::unrealized (GtkWidget *widget, gpointer user_data)
637 MoonWindowGtk* window = (MoonWindowGtk*)user_data;
639 window->SetCurrentDeployment ();
641 if (window->surface)
642 window->surface->HandleUIWindowUnavailable ();
644 #if SANITY
645 Deployment::SetCurrent (NULL);
646 #endif
648 return true;