qt: playlist: use item title if available
[vlc.git] / src / video_output / window.c
1 /*****************************************************************************
2  * window.c: "vout window" management
3  *****************************************************************************
4  * Copyright (C) 2009 Laurent Aimar
5  *
6  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include <assert.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include <vlc_common.h>
34 #include <vlc_vout_window.h>
35 #include <vlc_modules.h>
36 #include "inhibit.h"
37 #include <libvlc.h>
38
39 typedef struct
40 {
41     vout_window_t wnd;
42     module_t *module;
43     vlc_inhibit_t *inhibit;
44     bool inhibit_windowed;
45     bool active;
46     bool fullscreen;
47     vlc_mutex_t lock;
48 } window_t;
49
50 static int vout_window_start(void *func, bool forced, va_list ap)
51 {
52     int (*activate)(vout_window_t *) = func;
53     vout_window_t *wnd = va_arg(ap, vout_window_t *);
54
55     int ret = activate(wnd);
56     if (ret)
57         vlc_objres_clear(VLC_OBJECT(wnd));
58     (void) forced;
59     return ret;
60 }
61
62 vout_window_t *vout_window_New(vlc_object_t *obj, const char *module,
63                                const vout_window_owner_t *owner)
64 {
65     window_t *w = vlc_custom_create(obj, sizeof(*w), "window");
66     vout_window_t *window = &w->wnd;
67
68     memset(&window->handle, 0, sizeof(window->handle));
69     window->info.has_double_click = false;
70     window->sys = NULL;
71     assert(owner != NULL);
72     window->owner = *owner;
73
74     int dss = var_InheritInteger(obj, "disable-screensaver");
75
76     w->inhibit = NULL;
77     w->inhibit_windowed = dss == 1;
78     w->active = false;
79     w->fullscreen = false;
80     vlc_mutex_init(&w->lock);
81
82     w->module = vlc_module_load(window, "vout window", module, false,
83                                 vout_window_start, window);
84     if (!w->module) {
85         vlc_mutex_destroy(&w->lock);
86         vlc_object_delete(window);
87         return NULL;
88     }
89
90     /* Hook for screensaver inhibition */
91     if (dss > 0) {
92         vlc_inhibit_t *inh = vlc_inhibit_Create(VLC_OBJECT(window));
93
94         vlc_mutex_lock(&w->lock);
95         w->inhibit = inh;
96         vlc_mutex_unlock(&w->lock);
97     }
98     return window;
99 }
100
101 int vout_window_Enable(vout_window_t *window,
102                        const vout_window_cfg_t *restrict cfg)
103 {
104     if (window->ops->enable != NULL) {
105         int err = window->ops->enable(window, cfg);
106         if (err)
107             return err;
108     }
109
110     vout_window_SetInhibition(window, true);
111     return VLC_SUCCESS;
112 }
113
114 void vout_window_Disable(vout_window_t *window)
115 {
116     vout_window_SetInhibition(window, false);
117
118     if (window->ops->disable != NULL)
119         window->ops->disable(window);
120 }
121
122 void vout_window_Delete(vout_window_t *window)
123 {
124     if (!window)
125         return;
126
127     window_t *w = container_of(window, window_t, wnd);
128
129     if (w->inhibit != NULL) {
130         vlc_inhibit_t *inh = w->inhibit;
131
132         assert(!w->active);
133         vlc_mutex_lock(&w->lock);
134         w->inhibit = NULL;
135         vlc_mutex_unlock(&w->lock);
136
137         vlc_inhibit_Destroy(inh);
138     }
139
140     if (window->ops->destroy != NULL)
141         window->ops->destroy(window);
142
143     vlc_objres_clear(VLC_OBJECT(window));
144     vlc_mutex_destroy(&w->lock);
145     vlc_object_delete(window);
146 }
147
148 static void vout_window_UpdateInhibitionUnlocked(vout_window_t *window)
149 {
150     window_t *w = container_of(window, window_t, wnd);
151     unsigned flags = VLC_INHIBIT_NONE;
152
153     vlc_mutex_assert(&w->lock);
154
155     if (w->active && (w->inhibit_windowed || w->fullscreen))
156         flags = VLC_INHIBIT_VIDEO;
157     if (w->inhibit != NULL)
158         vlc_inhibit_Set(w->inhibit, flags);
159 }
160
161 void vout_window_SetInhibition(vout_window_t *window, bool enabled)
162 {
163     window_t *w = container_of(window, window_t, wnd);
164
165     vlc_mutex_lock(&w->lock);
166     w->active = enabled;
167
168     vout_window_UpdateInhibitionUnlocked(window);
169     vlc_mutex_unlock(&w->lock);
170 }
171
172 void vout_window_ReportWindowed(vout_window_t *window)
173 {
174     window_t *w = container_of(window, window_t, wnd);
175
176     if (!w->inhibit_windowed) {
177         vlc_mutex_lock(&w->lock);
178         w->fullscreen = false;
179
180         vout_window_UpdateInhibitionUnlocked(window);
181         vlc_mutex_unlock(&w->lock);
182     }
183
184     if (window->owner.cbs->windowed != NULL)
185         window->owner.cbs->windowed(window);
186 }
187
188 void vout_window_ReportFullscreen(vout_window_t *window, const char *id)
189 {
190     window_t *w = container_of(window, window_t, wnd);
191
192     if (!w->inhibit_windowed) {
193         vlc_mutex_lock(&w->lock);
194         w->fullscreen = true;
195         vout_window_UpdateInhibitionUnlocked(window);
196         vlc_mutex_unlock(&w->lock);
197     }
198
199     if (window->owner.cbs->fullscreened != NULL)
200         window->owner.cbs->fullscreened(window, id);
201 }
202
203 /* Video output display integration */
204 #include <vlc_vout.h>
205 #include <vlc_vout_display.h>
206 #include "window.h"
207 #include "vout_internal.h"
208
209 #define DOUBLE_CLICK_TIME VLC_TICK_FROM_MS(300)
210
211 typedef struct vout_display_window
212 {
213     vout_thread_t *vout;
214     vlc_mouse_t mouse;
215     vlc_tick_t last_left_press;
216 } vout_display_window_t;
217
218 static void vout_display_window_ResizeNotify(vout_window_t *window,
219                                              unsigned width, unsigned height)
220 {
221     vout_display_window_t *state = window->owner.sys;
222     vout_thread_t *vout = state->vout;
223
224     msg_Dbg(window, "resized to %ux%u", width, height);
225     vout_ChangeDisplaySize(vout, width, height);
226 }
227
228 static void vout_display_window_CloseNotify(vout_window_t *window)
229 {
230     /* TODO: Nowhere to dispatch to currently.
231      * Needs callback to ES output to deselect ES? */
232     msg_Err(window, "window closed");
233 }
234
235 static void vout_display_window_StateNotify(vout_window_t *window,
236                                             unsigned window_state)
237 {
238     vout_display_window_t *state = window->owner.sys;
239     vout_thread_t *vout = state->vout;
240
241     static const char states[][8] = {
242         [VOUT_WINDOW_STATE_NORMAL] = "normal",
243         [VOUT_WINDOW_STATE_ABOVE] = "above",
244         [VOUT_WINDOW_STATE_BELOW] = "below",
245     };
246
247     assert(window_state < ARRAY_SIZE(states));
248     msg_Dbg(window, "window state changed: %s", states[window_state]);
249     var_SetInteger(vout, "window-state", window_state);
250 }
251
252 static void vout_display_window_FullscreenNotify(vout_window_t *window,
253                                                  const char *id)
254 {
255     vout_display_window_t *state = window->owner.sys;
256     vout_thread_t *vout = state->vout;
257
258     msg_Dbg(window, (id != NULL) ? "window set to fullscreen on %s"
259                                  : "window set to fullscreen", id);
260     var_SetString(vout, "window-fullscreen-output",
261                   (id != NULL) ? id : "");
262     var_SetBool(vout, "window-fullscreen", true);
263 }
264
265 static void vout_display_window_WindowingNotify(vout_window_t *window)
266 {
267     vout_display_window_t *state = window->owner.sys;
268     vout_thread_t *vout = state->vout;
269
270     msg_Dbg(window, "window set windowed");
271     var_SetBool(vout, "window-fullscreen", false);
272 }
273
274 static void vout_display_window_MouseEvent(vout_window_t *window,
275                                            const vout_window_mouse_event_t *ev)
276 {
277     vout_display_window_t *state = window->owner.sys;
278     vout_thread_t *vout = state->vout;
279     vlc_mouse_t *m = &state->mouse;
280
281     m->b_double_click = false;
282
283     switch (ev->type)
284     {
285         case VOUT_WINDOW_MOUSE_MOVED:
286             vlc_mouse_SetPosition(m, ev->x, ev->y);
287             state->last_left_press = INT64_MIN;
288             break;
289
290         case VOUT_WINDOW_MOUSE_PRESSED:
291             if (!window->info.has_double_click
292              && ev->button_mask == MOUSE_BUTTON_LEFT
293              && !vlc_mouse_IsLeftPressed(m))
294             {
295                 const vlc_tick_t now = vlc_tick_now();
296
297                 if (state->last_left_press != INT64_MIN
298                  && now - state->last_left_press < DOUBLE_CLICK_TIME)
299                 {
300                     m->b_double_click = true;
301                     state->last_left_press = INT64_MIN;
302                 }
303                 else
304                     state->last_left_press = now;
305             }
306
307             vlc_mouse_SetPressed(m, ev->button_mask);
308             break;
309
310         case VOUT_WINDOW_MOUSE_RELEASED:
311             vlc_mouse_SetReleased(m, ev->button_mask);
312             break;
313
314         case VOUT_WINDOW_MOUSE_DOUBLE_CLICK:
315             assert(window->info.has_double_click);
316             m->b_double_click = true;
317             break;
318
319         default:
320             vlc_assert_unreachable();
321     }
322
323     vout_MouseState(vout, m);
324 }
325
326 static void vout_display_window_KeyboardEvent(vout_window_t *window,
327                                               unsigned key)
328 {
329     var_SetInteger(vlc_object_instance(window), "key-pressed", key);
330 }
331
332 static void vout_display_window_OutputEvent(vout_window_t *window,
333                                             const char *name, const char *desc)
334 {
335     if (desc != NULL)
336         msg_Dbg(window, "fullscreen output %s (%s) added", name, desc);
337     else
338         msg_Dbg(window, "fullscreen output %s removed", name);
339 }
340
341 static const struct vout_window_callbacks vout_display_window_cbs = {
342     .resized = vout_display_window_ResizeNotify,
343     .closed = vout_display_window_CloseNotify,
344     .state_changed = vout_display_window_StateNotify,
345     .fullscreened = vout_display_window_FullscreenNotify,
346     .windowed = vout_display_window_WindowingNotify,
347     .mouse_event = vout_display_window_MouseEvent,
348     .keyboard_event = vout_display_window_KeyboardEvent,
349     .output_event = vout_display_window_OutputEvent,
350 };
351
352 /**
353  * Creates a video window, initially without any attached display.
354  */
355 vout_window_t *vout_display_window_New(vout_thread_t *vout)
356 {
357     vout_display_window_t *state = malloc(sizeof (*state));
358     if (state == NULL)
359         return NULL;
360
361     vlc_mouse_Init(&state->mouse);
362     state->last_left_press = INT64_MIN;
363     state->vout = vout;
364
365     char *modlist = var_InheritString(vout, "window");
366     vout_window_owner_t owner = {
367         .cbs = &vout_display_window_cbs,
368         .sys = state,
369     };
370     vout_window_t *window;
371
372     var_Create(vout, "window-state", VLC_VAR_INTEGER);
373     var_Create(vout, "window-fullscreen", VLC_VAR_BOOL);
374     var_Create(vout, "window-fullscreen-output", VLC_VAR_STRING);
375
376     window = vout_window_New((vlc_object_t *)vout, modlist, &owner);
377     free(modlist);
378     if (window == NULL)
379         free(state);
380     return window;
381 }
382
383 /**
384  * Destroys a video window.
385  * \note The window must be detached.
386  */
387 void vout_display_window_Delete(vout_window_t *window)
388 {
389     vout_display_window_t *state = window->owner.sys;
390     vout_thread_t *vout = state->vout;
391
392     vout_window_Delete(window);
393     var_Destroy(vout, "window-fullscreen-output");
394     var_Destroy(vout, "window-fullscreen");
395     var_Destroy(vout, "window-state");
396     free(state);
397 }