1 /* GST123 - GStreamer based command line media player
2  * Copyright (C) 2010 Stefan Westerfeld
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General
15  * Public License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include "gtkinterface.h"
21 
22 #include <gtk/gtk.h>
23 #include <X11/Xlib.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gdk/gdkx.h>
26 #include <stdlib.h>
27 
28 using std::string;
29 
30 static gboolean
key_press_event_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)31 key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
32 {
33   GtkInterface *gtk_interface = static_cast<GtkInterface *> (data);
34 
35   return gtk_interface->handle_keypress_event (event);
36 }
37 
38 static gboolean
motion_notify_event_cb(GtkWidget * widget,GdkEventMotion * event,gpointer data)39 motion_notify_event_cb (GtkWidget *widget, GdkEventMotion *event, gpointer data)
40 {
41   GtkInterface *gtk_interface = static_cast<GtkInterface *> (data);
42 
43   return gtk_interface->handle_motion_notify_event (event);
44 }
45 
46 static gboolean
timeout_cb(gpointer data)47 timeout_cb (gpointer data)
48 {
49   GtkInterface *gtk_interface = static_cast<GtkInterface *> (data);
50 
51   return gtk_interface->handle_timeout();
52 }
53 
54 static gboolean
close_cb(GtkWidget * widget,GdkEvent * event,gpointer data)55 close_cb (GtkWidget *widget,
56           GdkEvent  *event,
57           gpointer   data)
58 {
59   GtkInterface *gtk_interface = static_cast<GtkInterface *> (data);
60 
61   return gtk_interface->handle_close();
62 }
63 
64 bool
have_x11_display()65 GtkInterface::have_x11_display()
66 {
67   static Display *display = NULL;
68 
69   if (!display)
70     display = XOpenDisplay (NULL);   // this should work if and only if we do have an X11 server we can use
71 
72   return display != NULL;
73 }
74 
75 void
init(int * argc,char *** argv,KeyHandler * handler)76 GtkInterface::init (int *argc, char ***argv, KeyHandler *handler)
77 {
78   key_handler = handler;
79 
80   if (have_x11_display())
81     {
82       gtk_init (argc, argv);
83       gtk_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
84       g_signal_connect (G_OBJECT (gtk_window), "key-press-event", G_CALLBACK (key_press_event_cb), this);
85       g_signal_connect (G_OBJECT (gtk_window), "motion-notify-event", G_CALLBACK (motion_notify_event_cb), this);
86       g_signal_connect (G_OBJECT (gtk_window), "delete-event", G_CALLBACK  (close_cb), this);
87       g_object_set (G_OBJECT (gtk_window), "events", GDK_POINTER_MOTION_MASK, NULL);
88 
89       // make background black
90       GdkColor color;
91       gdk_color_parse ("black", &color);
92       gtk_widget_modify_bg (gtk_window, GTK_STATE_NORMAL, &color);
93 
94       visible_cursor = NULL;
95       invisible_cursor = gdk_cursor_new (GDK_BLANK_CURSOR);
96 
97       cursor_timeout = 3;
98       g_timeout_add (500, (GSourceFunc) timeout_cb, this);
99     }
100   else
101     {
102       gtk_window = NULL;
103     }
104   gtk_window_visible = false;
105 
106   /* initialize map from Gdk keysyms to KeyHandler codes */
107   key_map[GDK_Page_Up]     = KEY_HANDLER_PAGE_UP;
108   key_map[GDK_Page_Down]   = KEY_HANDLER_PAGE_DOWN;
109   key_map[GDK_Left]        = KEY_HANDLER_LEFT;
110   key_map[GDK_Right]       = KEY_HANDLER_RIGHT;
111   key_map[GDK_Up]          = KEY_HANDLER_UP;
112   key_map[GDK_Down]        = KEY_HANDLER_DOWN;
113   key_map[GDK_KP_Add]      = '+';
114   key_map[GDK_KP_Subtract] = '-';
115 }
116 
117 void
unfullscreen()118 GtkInterface::unfullscreen()
119 {
120   if (gtk_window != NULL && gtk_window_visible)
121     gtk_window_unfullscreen (GTK_WINDOW (gtk_window));
122 }
123 
124 void
toggle_fullscreen()125 GtkInterface::toggle_fullscreen()
126 {
127   if (gtk_window != NULL && gtk_window_visible)
128     {
129       GdkWindowState state = gdk_window_get_state (GDK_WINDOW (gtk_window->window));
130       gboolean isFullscreen = ((state & GDK_WINDOW_STATE_FULLSCREEN) == GDK_WINDOW_STATE_FULLSCREEN);
131       if (isFullscreen)
132         gtk_window_unfullscreen (GTK_WINDOW (gtk_window));
133       else
134         gtk_window_fullscreen (GTK_WINDOW (gtk_window));
135     }
136 }
137 
138 bool
init_ok()139 GtkInterface::init_ok()
140 {
141   return gtk_window != NULL;
142 }
143 
144 GtkWidget*
window()145 GtkInterface::window()
146 {
147   return gtk_window;
148 }
149 
150 void
show()151 GtkInterface::show()
152 {
153   if (gtk_window != NULL && !gtk_window_visible)
154     {
155       gtk_widget_show_all (gtk_window);
156 
157       // get cursor, so we can restore it after hiding it
158       if (!visible_cursor)
159         visible_cursor = gdk_window_get_cursor (GDK_WINDOW (gtk_window->window));
160 
161       // sync, to make the window really visible before we return
162       gdk_display_sync (gdk_display_get_default());
163 
164       screen_saver (SUSPEND);
165       gtk_window_visible = true;
166     }
167 }
168 
169 void
hide()170 GtkInterface::hide()
171 {
172   if (gtk_window != NULL && gtk_window_visible)
173     {
174       gtk_widget_hide_all (gtk_window);
175       screen_saver (RESUME);
176       gtk_window_visible = false;
177     }
178 }
179 
180 void
end()181 GtkInterface::end()
182 {
183   if (gtk_window != NULL)
184     {
185       screen_saver (RESUME);
186     }
187 }
188 
189 
190 void
resize(int x,int y)191 GtkInterface::resize (int x, int y)
192 {
193   if (gtk_window != NULL)
194     gtk_window_resize (GTK_WINDOW (gtk_window), x, y);
195 }
196 
197 bool
handle_keypress_event(GdkEventKey * event)198 GtkInterface::handle_keypress_event (GdkEventKey *event)
199 {
200   int ch = 0;
201 
202   if (event->keyval > 0 && event->keyval <= 127)
203     ch = event->keyval;
204   else
205     ch = key_map[event->keyval];
206 
207   if (ch != 0)
208     {
209       key_handler->process_input (ch);
210       return true;
211     }
212   return false;
213 }
214 
215 void
set_title(const string & title)216 GtkInterface::set_title (const string& title)
217 {
218   if (gtk_window != NULL)
219     gtk_window_set_title (GTK_WINDOW (gtk_window), title.c_str());
220 }
221 
222 bool
handle_timeout()223 GtkInterface::handle_timeout()
224 {
225   if (gtk_window != NULL && gtk_window_visible)
226     {
227       if (cursor_timeout == 0)
228         {
229           gdk_window_set_cursor (GDK_WINDOW (gtk_window->window), invisible_cursor);
230           cursor_timeout = -1;
231         }
232       else if (cursor_timeout > 0)
233         {
234           cursor_timeout--;
235         }
236     }
237   return true;
238 }
239 
240 bool
handle_motion_notify_event(GdkEventMotion * event)241 GtkInterface::handle_motion_notify_event (GdkEventMotion *event)
242 {
243   if (gtk_window != NULL && gtk_window_visible)
244     {
245       gdk_window_set_cursor (GDK_WINDOW (gtk_window->window), visible_cursor);
246       cursor_timeout = 3;
247     }
248   return true;
249 }
250 
251 bool
handle_close()252 GtkInterface::handle_close()
253 {
254   g_return_val_if_fail (gtk_window != NULL, true);
255 
256   // quit on close
257   key_handler->process_input ('q');
258 
259   return true;
260 }
261 
262 void
screen_saver(ScreenSaverSetting setting)263 GtkInterface::screen_saver (ScreenSaverSetting setting)
264 {
265   GdkWindow *window = GTK_WIDGET (gtk_window)->window;
266   if (gtk_window != NULL && window)
267     {
268       guint64 wid = GDK_WINDOW_XWINDOW (window);
269 
270       const char *setting_str = (setting == SUSPEND) ? "suspend" : "resume";
271 
272       char *cmd = g_strdup_printf ("xdg-screensaver %s %" G_GUINT64_FORMAT " >/dev/null 2>&1", setting_str, wid);
273       int rc = system (cmd);   // don't complain if xdg-screensaver is not installed
274       (void) rc;
275       g_free (cmd);
276     }
277 }
278