qt.cpp 26.9 KB
Newer Older
Clément Stenac's avatar
Clément Stenac committed
1
/*****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
2
 * qt.cpp : Qt interface
Clément Stenac's avatar
Clément Stenac committed
3
 ****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright © 2006-2009 the VideoLAN team
5
 * $Id$
Clément Stenac's avatar
Clément Stenac committed
6 7
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
8
 *          Jean-Baptiste Kempf <jb@videolan.org>
Clément Stenac's avatar
Clément Stenac committed
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
 *****************************************************************************/
24

25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
Clément Stenac's avatar
Clément Stenac committed
28

29 30
#define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS

Clément Stenac's avatar
Clément Stenac committed
31
#include <QApplication>
32
#include <QDate>
33
#include <QMutex>
Clément Stenac's avatar
Clément Stenac committed
34

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
35
#include "qt.hpp"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
36

37
#include "input_manager.hpp"    /* THEMIM destruction */
38
#include "dialogs_provider.hpp" /* THEDP creation */
39 40 41 42 43
#ifdef _WIN32
# include "main_interface_win32.hpp"
#else
# include "main_interface.hpp"   /* MainInterface creation */
#endif
44
#include "extensions_manager.hpp" /* Extensions manager */
45
#include "managers/addons_manager.hpp" /* Addons manager */
46 47
#include "dialogs/help.hpp"     /* Launch Update */
#include "recents.hpp"          /* Recents Item destruction */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
48
#include "util/qvlcapp.hpp"     /* QVLCApplication definition */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
49
#include "components/playlist/playlist_model.hpp" /* for ~PLModel() */
Clément Stenac's avatar
Clément Stenac committed
50

51
#include <vlc_plugin.h>
52
#include <vlc_vout_window.h>
Clément Stenac's avatar
Clément Stenac committed
53

54
#ifdef _WIN32 /* For static builds */
55
 #include <QtPlugin>
56
 #if HAS_QT5
57 58
  #ifdef QT_STATICPLUGIN
   Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
59 60 61
   #if !HAS_QT56
    Q_IMPORT_PLUGIN(AccessibleFactory)
   #endif
62
  #endif
63 64 65 66
 #else
  Q_IMPORT_PLUGIN(qjpeg)
  Q_IMPORT_PLUGIN(qtaccessiblewidgets)
 #endif
67 68
#endif

Clément Stenac's avatar
Clément Stenac committed
69 70 71
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
72
static int  OpenIntf     ( vlc_object_t * );
Clément Stenac's avatar
Clément Stenac committed
73
static int  OpenDialogs  ( vlc_object_t * );
74 75
static int  Open         ( vlc_object_t *, bool );
static void Close        ( vlc_object_t * );
76 77
static int  WindowOpen   ( vout_window_t *, const vout_window_cfg_t * );
static void WindowClose  ( vout_window_t * );
Clément Stenac's avatar
Clément Stenac committed
78 79 80 81 82
static void ShowDialog   ( intf_thread_t *, int, int, intf_dialog_args_t * );

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
83
#define ADVANCED_PREFS_TEXT N_( "Show advanced preferences over simple ones" )
84 85
#define ADVANCED_PREFS_LONGTEXT N_( "Show advanced preferences and not simple "\
                                    "preferences when opening the preferences "\
86
                                    "dialog." )
87

88 89 90
#define SYSTRAY_TEXT N_( "Systray icon" )
#define SYSTRAY_LONGTEXT N_( "Show an icon in the systray " \
                             "allowing you to control VLC media player " \
91
                             "for basic actions." )
92

93
#define MINIMIZED_TEXT N_( "Start VLC with only a systray icon" )
94 95
#define MINIMIZED_LONGTEXT N_( "VLC will start with just an icon in " \
                               "your taskbar" )
96

97 98 99 100 101
#define KEEPSIZE_TEXT N_( "Resize interface to the native video size" )
#define KEEPSIZE_LONGTEXT N_( "You have two choices:\n" \
            " - The interface will resize to the native video size\n" \
            " - The video will fit to the interface size\n " \
            "By default, interface resize to the native video size." )
102

103 104
#define TITLE_TEXT N_( "Show playing item name in window title" )
#define TITLE_LONGTEXT N_( "Show the name of the song or video in the " \
Kaarlo Raiha's avatar
Kaarlo Raiha committed
105
                           "controller window title." )
106

107
#define NOTIFICATION_TEXT N_( "Show notification popup on track change" )
108 109
#define NOTIFICATION_LONGTEXT N_( \
    "Show a notification popup with the artist and track name when " \
110
    "the current playlist item changes, when VLC is minimized or hidden." )
111

112
#define OPACITY_TEXT N_( "Windows opacity between 0.1 and 1" )
113
#define OPACITY_LONGTEXT N_( "Sets the windows opacity between 0.1 and 1 " \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
114
                             "for main interface, playlist and extended panel."\
115 116
                             " This option only works with Windows and " \
                             "X11 with composite extensions." )
117

Christophe Mutricy's avatar
Typo  
Christophe Mutricy committed
118
#define OPACITY_FS_TEXT N_( "Fullscreen controller opacity between 0.1 and 1" )
119 120 121 122 123 124
#define OPACITY_FS_LONGTEXT N_( "Sets the fullscreen controller opacity between 0.1 and 1 " \
                             "for main interface, playlist and extended panel."\
                             " This option only works with Windows and " \
                             "X11 with composite extensions." )


125
#define ERROR_TEXT N_( "Show unimportant error and warnings dialogs" )
126

127
#define UPDATER_TEXT N_( "Activate the updates availability notification" )
128
#define UPDATER_LONGTEXT N_( "Activate the automatic notification of new " \
129 130
                            "versions of the software. It runs once every " \
                            "two weeks." )
Antoine Cellerier's avatar
Antoine Cellerier committed
131
#define UPDATER_DAYS_TEXT N_("Number of days between two update checks")
132

133
#define PRIVACY_TEXT N_( "Ask for network policy at start" )
134

Ludovic Fauvet's avatar
Ludovic Fauvet committed
135
#define RECENTPLAY_TEXT N_( "Save the recently played items in the menu" )
136

Ludovic Fauvet's avatar
Ludovic Fauvet committed
137
#define RECENTPLAY_FILTER_TEXT N_( "List of words separated by | to filter" )
138 139
#define RECENTPLAY_FILTER_LONGTEXT N_( "Regular expression used to filter " \
        "the recent items played in the player" )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
140

141 142 143 144
#define SLIDERCOL_TEXT N_( "Define the colors of the volume slider " )
#define SLIDERCOL_LONGTEXT N_( "Define the colors of the volume slider\n" \
                       "By specifying the 12 numbers separated by a ';'\n" \
            "Default is '255;255;255;20;226;20;255;176;15;235;30;20'\n" \
145
            "An alternative can be '30;30;50;40;40;100;50;50;160;150;150;255' ")
146 147 148 149

#define QT_MODE_TEXT N_( "Selection of the starting mode and look " )
#define QT_MODE_LONGTEXT N_( "Start VLC with:\n" \
                             " - normal mode\n"  \
150
                             " - a zone always present to show information " \
151 152 153
                                  "as lyrics, album arts...\n" \
                             " - minimal mode with limited controls" )

154
#define QT_FULLSCREEN_TEXT N_( "Show a controller in fullscreen mode" )
Rémi Denis-Courmont's avatar
English  
Rémi Denis-Courmont committed
155
#define QT_NATIVEOPEN_TEXT N_( "Embed the file browser in open dialog" )
156

157
#define FULLSCREEN_NUMBER_TEXT N_( "Define which screen fullscreen goes" )
Kaarlo Raiha's avatar
Kaarlo Raiha committed
158
#define FULLSCREEN_NUMBER_LONGTEXT N_( "Screennumber of fullscreen, instead of " \
159 160
                                       "same screen where interface is" )

161 162 163 164
#define QT_AUTOLOAD_EXTENSIONS_TEXT N_( "Load extensions on startup" )
#define QT_AUTOLOAD_EXTENSIONS_LONGTEXT N_( "Automatically load the "\
                                            "extensions module on startup" )

165
#define QT_MINIMAL_MODE_TEXT N_("Start in minimal view (without menus)" )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
166

167 168
#define QT_BGCONE_TEXT N_( "Display background cone or art" )
#define QT_BGCONE_LONGTEXT N_( "Display background cone or current album art " \
169
                            "when not playing. " \
170 171 172
                            "Can be disabled to prevent burning screen." )
#define QT_BGCONE_EXPANDS_TEXT N_( "Expanding background cone or art." )
#define QT_BGCONE_EXPANDS_LONGTEXT N_( "Background art fits window's size" )
173 174 175 176 177 178 179

#define QT_DISABLE_VOLUME_KEYS_TEXT N_( "Ignore keyboard volume buttons." )
#define QT_DISABLE_VOLUME_KEYS_LONGTEXT N_(                                             \
    "With this option checked, the volume up, volume down and mute buttons on your "    \
    "keyboard will always change your system volume. With this option unchecked, the "  \
    "volume buttons will change VLC's volume when VLC is selected and change the "      \
    "system volume when VLC is not selected." )
180 181 182 183 184

#define QT_PAUSE_MINIMIZED_TEXT N_( "Pause the video playback when minimized" )
#define QT_PAUSE_MINIMIZED_LONGTEXT N_( \
    "With this option enabled, the playback will be automatically paused when minimizing the window." )

185 186 187 188
#define ICONCHANGE_TEXT N_( "Allow automatic icon changes")
#define ICONCHANGE_LONGTEXT N_( \
    "This option allows the interface to change its icon on various occasions.")

189 190
#define VOLUME_MAX_TEXT N_( "Maximum Volume displayed" )

191 192
#define FULLSCREEN_CONTROL_PIXELS N_( "Fullscreen controller mouse sensitivity" )

193 194
#define CONTINUE_PLAYBACK_TEXT N_("Continue playback?")

195 196 197 198 199 200
static const int i_notification_list[] =
    { NOTIFICATION_NEVER, NOTIFICATION_MINIMIZED, NOTIFICATION_ALWAYS };

static const char *const psz_notification_list_text[] =
    { N_("Never"), N_("When minimized"), N_("Always") };

201 202 203 204 205 206 207
static const int i_continue_list[] =
    { 0, 1, 2 };

static const char *const psz_continue_list_text[] =
    { N_("Never"), N_("Ask"), N_("Always") };


Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
208
/**********************************************************************/
209 210 211
vlc_module_begin ()
    set_shortname( "Qt" )
    set_description( N_("Qt interface") )
212
    set_category( CAT_INTERFACE )
213 214
    set_subcategory( SUBCAT_INTERFACE_MAIN )
    set_capability( "interface", 151 )
215
    set_callbacks( OpenIntf, Close )
216 217

    add_shortcut("qt")
218

219
    add_bool( "qt-minimal-view", false, QT_MINIMAL_MODE_TEXT,
220
              QT_MINIMAL_MODE_TEXT, false );
221

222
    add_bool( "qt-system-tray", true, SYSTRAY_TEXT, SYSTRAY_LONGTEXT, false)
223 224 225 226 227 228

    add_integer( "qt-notification", NOTIFICATION_MINIMIZED,
                 NOTIFICATION_TEXT,
                 NOTIFICATION_LONGTEXT, false )
            change_integer_list( i_notification_list, psz_notification_list_text )

229 230
    add_bool( "qt-start-minimized", false, MINIMIZED_TEXT,
              MINIMIZED_LONGTEXT, true)
231
    add_bool( "qt-pause-minimized", false, QT_PAUSE_MINIMIZED_TEXT,
232
              QT_PAUSE_MINIMIZED_LONGTEXT, false )
233

234
    add_float_with_range( "qt-opacity", 1., 0.1, 1., OPACITY_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
235
                          OPACITY_LONGTEXT, false )
236
    add_float_with_range( "qt-fs-opacity", 0.8, 0.1, 1., OPACITY_FS_TEXT,
237
                          OPACITY_FS_LONGTEXT, false )
238

239
    add_bool( "qt-video-autoresize", true, KEEPSIZE_TEXT,
240
              KEEPSIZE_LONGTEXT, false )
241
    add_bool( "qt-name-in-title", true, TITLE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
242
              TITLE_LONGTEXT, false )
243
    add_bool( "qt-fs-controller", true, QT_FULLSCREEN_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
244
              QT_FULLSCREEN_TEXT, false )
245

246 247 248 249
    add_bool( "qt-recentplay", true, RECENTPLAY_TEXT,
              RECENTPLAY_TEXT, false )
    add_string( "qt-recentplay-filter", "",
                RECENTPLAY_FILTER_TEXT, RECENTPLAY_FILTER_LONGTEXT, false )
250 251
    add_integer( "qt-continue", 1, CONTINUE_PLAYBACK_TEXT, CONTINUE_PLAYBACK_TEXT, false )
            change_integer_list(i_continue_list, psz_continue_list_text )
252 253 254 255

#ifdef UPDATE_CHECK
    add_bool( "qt-updates-notif", true, UPDATER_TEXT,
              UPDATER_LONGTEXT, false )
256 257
    add_integer_with_range( "qt-updates-days", 3, 0, 180,
              UPDATER_DAYS_TEXT, UPDATER_DAYS_TEXT, false )
258 259
#endif

260
#ifdef _WIN32
261 262 263 264 265 266 267
    add_bool( "qt-disable-volume-keys"             /* name */,
              true                                 /* default value */,
              QT_DISABLE_VOLUME_KEYS_TEXT          /* text */,
              QT_DISABLE_VOLUME_KEYS_LONGTEXT      /* longtext */,
              false                                /* advanced mode only */)
#endif

268
    add_bool( "qt-embedded-open", false, QT_NATIVEOPEN_TEXT,
269
               QT_NATIVEOPEN_TEXT, false )
270

271

272
    add_bool( "qt-advanced-pref", false, ADVANCED_PREFS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
273
              ADVANCED_PREFS_LONGTEXT, false )
274
    add_bool( "qt-error-dialogs", true, ERROR_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
275
              ERROR_TEXT, false )
276

277
    add_string( "qt-slider-colours", "153;210;153;20;210;20;255;199;15;245;39;29",
278
                SLIDERCOL_TEXT, SLIDERCOL_LONGTEXT, false )
279

280
    add_bool( "qt-privacy-ask", true, PRIVACY_TEXT, PRIVACY_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
281
              false )
282
        change_private ()
283

284
    add_integer( "qt-fullscreen-screennumber", -1, FULLSCREEN_NUMBER_TEXT,
285 286
               FULLSCREEN_NUMBER_LONGTEXT, false );

287
    add_bool( "qt-autoload-extensions", true,
288 289 290
              QT_AUTOLOAD_EXTENSIONS_TEXT, QT_AUTOLOAD_EXTENSIONS_LONGTEXT,
              false )

291 292 293 294
    add_bool( "qt-bgcone", true, QT_BGCONE_TEXT, QT_BGCONE_LONGTEXT, true )
    add_bool( "qt-bgcone-expands", false, QT_BGCONE_EXPANDS_TEXT,
              QT_BGCONE_EXPANDS_LONGTEXT, true )

295
    add_bool( "qt-icon-change", true, ICONCHANGE_TEXT, ICONCHANGE_LONGTEXT, true )
296

297 298
    add_integer_with_range( "qt-max-volume", 125, 60, 300, VOLUME_MAX_TEXT, VOLUME_MAX_TEXT, true)

299 300 301
    add_integer_with_range( "qt-fs-sensitivity", 3, 0, 4000, FULLSCREEN_CONTROL_PIXELS,
            FULLSCREEN_CONTROL_PIXELS, true)

302
    add_obsolete_bool( "qt-blingbling" )      /* Suppressed since 1.0.0 */
303
    add_obsolete_integer( "qt-display-mode" ) /* Suppressed since 1.1.0 */
304

305 306 307
    add_obsolete_bool( "qt-adv-options" )     /* Since 2.0.0 */
    add_obsolete_bool( "qt-volume-complete" ) /* Since 2.0.0 */
    add_obsolete_integer( "qt-startvolume" )  /* Since 2.0.0 */
308

309
    cannot_unload_broken_library()
310

311 312 313
    add_submodule ()
        set_description( "Dialogs provider" )
        set_capability( "dialogs provider", 51 )
314

315
        set_callbacks( OpenDialogs, Close )
316

317
    add_submodule ()
318
        set_capability( "vout window", 0 )
319
        set_callbacks( WindowOpen, WindowClose )
320

321
vlc_module_end ()
Clément Stenac's avatar
Clément Stenac committed
322

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
323 324
/*****************************************/

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
325
/* Ugly, but the Qt interface assumes single instance anyway */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
326
static vlc_sem_t ready;
327 328 329
static QMutex lock;
static bool busy = false;
static bool active = false;
330

Clément Stenac's avatar
Clément Stenac committed
331 332 333
/*****************************************************************************
 * Module callbacks
 *****************************************************************************/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
334

335 336 337 338 339 340 341
static void *ThreadPlatform( void *, char * );

static void *Thread( void *data )
{
    return ThreadPlatform( data, NULL );
}

342
#ifdef Q_OS_MAC
343
/* Used to abort the app.exec() on OSX after libvlc_Quit is called */
344
#include "../../../lib/libvlc_internal.h" /* libvlc_SetExitHandler */
345 346 347 348 349 350
static void Abort( void *obj )
{
    QVLCApp::triggerQuit();
}
#endif

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
#if defined (QT5_HAS_X11)
# include <vlc_xlib.h>

static void *ThreadXCB( void *data )
{
    char platform_name[] = "xcb";
    return ThreadPlatform( data, platform_name );
}

static bool HasX11( vlc_object_t *obj )
{
    if( !vlc_xlib_init( obj ) )
        return false;

    Display *dpy = XOpenDisplay( NULL );
    if( dpy == NULL )
        return false;

    XCloseDisplay( dpy );
    return true;
}
#endif

#ifdef QT5_HAS_WAYLAND
# include <wayland-client.h>

static void *ThreadWayland( void *data )
{
    char platform_name[] = "wayland";
    return ThreadPlatform( data, platform_name );
}

static bool HasWayland( void )
{
    struct wl_display *dpy = wl_display_connect( NULL );
    if( dpy == NULL )
        return false;

    wl_display_disconnect( dpy );
    return true;
}
#endif

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
394
/* Open Interface */
395
static int Open( vlc_object_t *p_this, bool isDialogProvider )
Clément Stenac's avatar
Clément Stenac committed
396 397
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
398
    void *(*thread)(void *) = Thread;
399

400 401 402 403 404 405 406 407 408 409 410
#ifdef QT5_HAS_WAYLAND
    if( HasWayland() )
        thread = ThreadWayland;
    else
#endif
#ifdef QT5_HAS_X11
    if( HasX11( p_this ) )
        thread = ThreadXCB;
    else
#endif
#if defined (QT5_HAS_X11) || defined (QT5_HAS_WAYLAND)
411
        return VLC_EGENERIC;
412
#endif
413

414
    QMutexLocker locker (&lock);
415
    if (busy)
416
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
417
        msg_Err (p_this, "cannot start Qt multiple times");
418 419 420
        return VLC_EGENERIC;
    }

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
421 422
    /* Allocations of p_sys */
    intf_sys_t *p_sys = p_intf->p_sys = new intf_sys_t;
423
    p_sys->b_isDialogProvider = isDialogProvider;
424
    p_sys->p_mi = NULL;
425
    p_sys->pl_model = NULL;
426

427 428
    /* set up the playlist to work on */
    if( isDialogProvider )
429
        p_sys->p_playlist = pl_Get( (intf_thread_t *)p_intf->obj.parent );
430
    else
431
        p_sys->p_playlist = pl_Get( p_intf );
432

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
433
    /* */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
434
    vlc_sem_init (&ready, 0);
435
#ifdef Q_OS_MAC
436
    /* Run mainloop on the main thread as Cocoa requires */
437
    libvlc_SetExitHandler( p_intf->obj.libvlc, Abort, p_intf );
438
    thread( (void *)p_intf );
439
#else
440
    if( vlc_clone( &p_sys->thread, thread, p_intf, VLC_THREAD_PRIORITY_LOW ) )
441 442
    {
        delete p_sys;
Christophe Mutricy's avatar
Christophe Mutricy committed
443
        return VLC_ENOMEM;
444
    }
445
#endif
446

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
447 448 449
    /* Wait for the interface to be ready. This prevents the main
     * LibVLC thread from starting video playback before we can create
     * an embedded video window. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
450 451
    vlc_sem_wait (&ready);
    vlc_sem_destroy (&ready);
452
    busy = active = true;
453

Clément Stenac's avatar
Clément Stenac committed
454 455 456
    return VLC_SUCCESS;
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
457
/* Open Qt interface */
458 459 460 461 462
static int OpenIntf( vlc_object_t *p_this )
{
    return Open( p_this, false );
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
463
/* Open Dialog Provider */
Clément Stenac's avatar
Clément Stenac committed
464 465
static int OpenDialogs( vlc_object_t *p_this )
{
466
    return Open( p_this, true );
Clément Stenac's avatar
Clément Stenac committed
467 468 469 470 471
}

static void Close( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
472 473
    intf_sys_t *p_sys = p_intf->p_sys;

474
    if( !p_sys->b_isDialogProvider )
475
    {
476
        playlist_t *pl = THEPL;
477 478

        playlist_Deactivate (pl); /* release window provider if needed */
479
    }
480

481
    /* And quit */
482
    msg_Dbg( p_this, "requesting exit..." );
483
    QVLCApp::triggerQuit();
484

485
    msg_Dbg( p_this, "waiting for UI thread..." );
486
#ifndef Q_OS_MAC
487
    vlc_join (p_sys->thread, NULL);
488
#endif
489
    delete p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
490

491 492 493
    QMutexLocker locker (&lock);
    assert (busy);
    busy = false;
Clément Stenac's avatar
Clément Stenac committed
494 495
}

496
static void *ThreadPlatform( void *obj, char *platform_name )
Clément Stenac's avatar
Clément Stenac committed
497
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
498
    intf_thread_t *p_intf = (intf_thread_t *)obj;
499
    intf_sys_t *p_sys = p_intf->p_sys;
500 501
    char vlc_name[] = "vlc"; /* for WM_CLASS */
    char platform_parm[] = "-platform";
502 503 504 505 506 507 508 509 510 511
    char *argv[4];
    int argc = 0;

    argv[argc++] = vlc_name;
    if( platform_name != NULL )
    {
        argv[argc++] = platform_parm;
        argv[argc++] = platform_name;
    }
    argv[argc] = NULL;
512

Rafaël Carré's avatar
Rafaël Carré committed
513
    Q_INIT_RESOURCE( vlc );
514

515 516 517 518
#if HAS_QT56
    QApplication::setAttribute( Qt::AA_EnableHighDpiScaling );
#endif

519
    /* Start the QApplication here */
520
    QVLCApp app( argc, argv );
521

Fahad Al-Saidi's avatar
Fahad Al-Saidi committed
522 523 524 525
    /* Set application direction to locale direction,
     * necessary for  RTL locales */
    app.setLayoutDirection(QLocale().textDirection());

526
    p_sys->p_app = &app;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
527

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
528 529

    /* All the settings are in the .conf/.ini style */
Hannes Domani's avatar
Hannes Domani committed
530 531 532 533 534 535 536 537 538
#ifdef _WIN32
    char *cConfigDir = config_GetUserDir( VLC_CONFIG_DIR );
    QString configDir = cConfigDir;
    free( cConfigDir );
    if( configDir.endsWith( "\\vlc" ) )
        configDir.chop( 4 ); /* the "\vlc" dir is added again by QSettings */
    QSettings::setPath( QSettings::IniFormat, QSettings::UserScope, configDir );
#endif

539
    p_sys->mainSettings = new QSettings(
540
#ifdef _WIN32
541 542 543 544 545 546
            QSettings::IniFormat,
#else
            QSettings::NativeFormat,
#endif
            QSettings::UserScope, "vlc", "vlc-qt-interface" );

547
    /* Icon setting, Mac uses icon from .icns */
548
#ifndef Q_OS_MAC
549
    if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
550
        app.setWindowIcon( QIcon::fromTheme( "vlc-xmas", QIcon( ":/logo/vlc128-xmas.png" ) ) );
551
    else
552
        app.setWindowIcon( QIcon::fromTheme( "vlc", QIcon( ":/logo/vlc256.png" ) ) );
553
#endif
Rafaël Carré's avatar
Rafaël Carré committed
554

555
    /* Initialize the Dialog Provider and the Main Input Manager */
556
    DialogsProvider::getInstance( p_intf );
557 558
    MainInputManager* mim = MainInputManager::getInstance( p_intf );
    mim->probeCurrentInput();
Clément Stenac's avatar
Clément Stenac committed
559

560 561
#ifdef UPDATE_CHECK
    /* Checking for VLC updates */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
562 563
    if( var_InheritBool( p_intf, "qt-updates-notif" ) &&
        !var_InheritBool( p_intf, "qt-privacy-ask" ) )
564
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
565
        int interval = var_InheritInteger( p_intf, "qt-updates-days" );
566 567 568 569 570 571 572 573 574 575
        if( QDate::currentDate() >
             getSettings()->value( "updatedate" ).toDate().addDays( interval ) )
        {
            /* The constructor of the update Dialog will do the 1st request */
            UpdateDialog::getInstance( p_intf );
            getSettings()->setValue( "updatedate", QDate::currentDate() );
        }
    }
#endif

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
576
    /* Create the normal interface in non-DP mode */
577 578 579
    MainInterface *p_mi = NULL;

    if( !p_sys->b_isDialogProvider )
580
    {
581 582 583
#ifdef _WIN32
        p_mi = new MainInterfaceWin32( p_intf );
#else
584
        p_mi = new MainInterface( p_intf );
585
#endif
586
        p_sys->p_mi = p_mi;
587

588 589
        /* Check window type from the Qt platform back-end */
        p_sys->voutWindowType = VOUT_WINDOW_TYPE_INVALID;
590
#if HAS_QT5
591 592 593 594 595 596 597 598 599
        QString platform = app.platformName();
        if( platform == qfu("xcb") )
            p_sys->voutWindowType = VOUT_WINDOW_TYPE_XID;
        else if( platform == qfu("windows") )
            p_sys->voutWindowType = VOUT_WINDOW_TYPE_HWND;
        else if( platform == qfu("cocoa" ) )
            p_sys->voutWindowType = VOUT_WINDOW_TYPE_NSOBJECT;
        else
            msg_Err( p_intf, "unknown Qt platform: %s", qtu(platform) );
600
#elif defined (Q_WS_WIN) || defined (Q_WS_PM)
601
        p_sys->voutWindowType = VOUT_WINDOW_TYPE_HWND;
602
#elif defined (Q_WS_MAC)
603
        p_sys->voutWindowType = VOUT_WINDOW_TYPE_NSOBJECT;
604 605
#endif

606 607 608 609 610 611 612 613 614 615
        var_Create( THEPL, "qt4-iface", VLC_VAR_ADDRESS );
        var_SetAddress( THEPL, "qt4-iface", p_intf );
        var_Create( THEPL, "window", VLC_VAR_STRING );
        if( p_sys->voutWindowType != VOUT_WINDOW_TYPE_INVALID )
            var_SetString( THEPL, "window", "qt,any" );
    }

    /* Explain how to show a dialog :D */
    p_intf->pf_show_dialog = ShowDialog;

616
    /* Tell the main LibVLC thread we are ready */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
617
    vlc_sem_post (&ready);
618

619
#ifdef Q_OS_MAC
620
    /* We took over main thread, register and start here */
621
    if( !p_sys->b_isDialogProvider )
622
        playlist_Play( THEPL );
623 624
#endif

625
    /* Last settings */
626
    app.setQuitOnLastWindowClosed( false );
627

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
628
    /* Retrieve last known path used in file browsing */
629
    p_sys->filepath =
630
         getSettings()->value( "filedialog-path", QVLCUserDir( VLC_HOME_DIR ) ).toString();
631

632 633 634 635 636
    /* Loads and tries to apply the preferred QStyle */
    QString s_style = getSettings()->value( "MainWindow/QtStyle", "" ).toString();
    if( s_style.compare("") != 0 )
        QApplication::setStyle( s_style );

637
    /* Launch */
638
    app.exec();
639

640
    msg_Dbg( p_intf, "QApp exec() finished" );
641
    if (p_mi != NULL)
642
    {
643 644 645
        var_Destroy( THEPL, "window" );
        var_Destroy( THEPL, "qt4-iface" );

646 647
        QMutexLocker locker (&lock);
        active = false;
648

649
        p_sys->p_mi = NULL;
650 651
        /* Destroy first the main interface because it is connected to some
           slots in the MainInputManager */
652
        delete p_mi;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
653
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
654

655 656
    /* */
    ExtensionsManager::killInstance();
657
    AddonsManager::killInstance();
658

659 660 661 662 663
    /* Destroy all remaining windows,
       because some are connected to some slots
       in the MainInputManager
       Settings must be destroyed after that.
     */
Clément Stenac's avatar
Clément Stenac committed
664
    DialogsProvider::killInstance();
665

Ludovic Fauvet's avatar
Ludovic Fauvet committed
666 667 668
    /* Delete the recentsMRL object before the configuration */
    RecentsMRL::killInstance();

669 670
    /* Save the path or delete if recent play are disabled */
    if( var_InheritBool( p_intf, "qt-recentplay" ) )
671
        getSettings()->setValue( "filedialog-path", p_sys->filepath );
672 673
    else
        getSettings()->remove( "filedialog-path" );
674

675
    /* */
676
    delete p_sys->pl_model;
677

678 679 680
    /* Destroy the MainInputManager */
    MainInputManager::killInstance();

681
    /* Delete the configuration. Application has to be deleted after that. */
682
    delete p_sys->mainSettings;
683

684
    /* Delete the application automatically */
685
    return NULL;
Clément Stenac's avatar
Clément Stenac committed
686 687 688 689 690 691 692 693
}

/*****************************************************************************
 * Callback to show a dialog
 *****************************************************************************/
static void ShowDialog( intf_thread_t *p_intf, int i_dialog_event, int i_arg,
                        intf_dialog_args_t *p_arg )
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
694
    VLC_UNUSED( p_intf );
Clément Stenac's avatar
Clément Stenac committed
695
    DialogEvent *event = new DialogEvent( i_dialog_event, i_arg, p_arg );
696
    QApplication::postEvent( THEDP, event );
Clément Stenac's avatar
Clément Stenac committed
697
}
698

699 700
/**
 * Video output window provider
701 702
 *
 * TODO move it out of here ?
703
 */
704
static int WindowControl( vout_window_t *, int i_query, va_list );
705

706
static int WindowOpen( vout_window_t *p_wnd, const vout_window_cfg_t *cfg )
707
{
708
    if( cfg->is_standalone )
709 710
        return VLC_EGENERIC;

711 712
    intf_thread_t *p_intf =
        (intf_thread_t *)var_InheritAddress( p_wnd, "qt4-iface" );
713
    if( !p_intf )
714
    {   /* If another interface is used, this plugin cannot work */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
715
        msg_Dbg( p_wnd, "Qt interface not found" );
716 717
        return VLC_EGENERIC;
    }
718 719 720

    if( cfg->type != VOUT_WINDOW_TYPE_INVALID
     && cfg->type != p_intf->p_sys->voutWindowType )
721
        return VLC_EGENERIC;
722 723

    switch( p_intf->p_sys->voutWindowType )
724 725 726 727 728 729
    {
        case VOUT_WINDOW_TYPE_XID:
            if( var_InheritBool( p_wnd, "video-wallpaper" ) )
                return VLC_EGENERIC;
            break;
    }
730

731 732 733 734
    QMutexLocker locker (&lock);
    if (unlikely(!active))
        return VLC_EGENERIC;

735
    MainInterface *p_mi = p_intf->p_sys->p_mi;
736
    msg_Dbg( p_wnd, "requesting video window..." );
737

738 739
    unsigned i_width = cfg->width;
    unsigned i_height = cfg->height;
740

741
    WId wid = p_mi->getVideo( p_wnd, &i_width, &i_height, cfg->is_fullscreen );
742
    if( !wid )
743
        return VLC_EGENERIC;
744

745 746 747
    p_wnd->type = p_intf->p_sys->voutWindowType;

    switch( p_wnd->type )
748 749
    {
        case VOUT_WINDOW_TYPE_XID:
750
            p_wnd->handle.xid = (uintptr_t)wid;
751 752 753 754 755 756 757 758
            p_wnd->display.x11 = NULL;
            break;
        case VOUT_WINDOW_TYPE_HWND:
            p_wnd->handle.hwnd = (void *)wid;
            break;
        case VOUT_WINDOW_TYPE_NSOBJECT:
            p_wnd->handle.nsobject = (void *)wid;
            break;
759
        default:
760
            vlc_assert_unreachable();
761 762
    }

763 764
    p_wnd->control = WindowControl;
    p_wnd->sys = (vout_window_sys_t*)p_mi;
765 766 767
    return VLC_SUCCESS;
}

768
static int WindowControl( vout_window_t *p_wnd, int i_query, va_list args )
769
{
770
    MainInterface *p_mi = (MainInterface *)p_wnd->sys;
771 772 773 774 775 776 777
    QMutexLocker locker (&lock);

    if (unlikely(!active))
    {
        msg_Warn (p_wnd, "video already released before control");
        return VLC_EGENERIC;
    }
778
    return p_mi->controlVideo( i_query, args );
779 780
}

781
static void WindowClose( vout_window_t *p_wnd )
782
{
783
    MainInterface *p_mi = (MainInterface *)p_wnd->sys;
784 785 786
    QMutexLocker locker (&lock);

    /* Normally, the interface terminates after the video. In the contrary, the
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
787 788 789
     * Qt main loop is gone, so we cannot send any event to the user interface
     * widgets. Ideally, we would keep the Qt main loop running until after
     * the video window is released. But it is far simpler to just have the Qt
790 791 792 793 794 795 796
     * thread destroy the window early, and to turn this function into a stub.
     *
     * That assumes the video output will behave sanely if it window is
     * destroyed asynchronously.
     * XCB and Xlib-XCB are fine with that. Plain Xlib wouldn't, */
    if (unlikely(!active))
    {
797
        msg_Warn (p_wnd, "video already released");
798 799
        return;
    }
800
    msg_Dbg (p_wnd, "releasing video...");
801
    p_mi->releaseVideo();
802
}