view gui/interface.c @ 34338:4a507d3a039a

Add highly experimental support for OpenGL ES. It only supports EGL/X11, uses/supports only ES v1, will crash if certain features are used, compiling without desktop GL installed is not tested and possibly more caveats. However it is close enough to be able to display a video on a BeagleBoard via OpenGL. Performance could not be tested properly since I do not have a display that is compatible with the BeagleBoard output...
author reimar
date Sat, 10 Dec 2011 20:55:31 +0000
parents e8e4e6f9b7ac
children f05c75392897
line wrap: on
line source

/*
 * This file is part of MPlayer.
 *
 * MPlayer 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.
 *
 * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "interface.h"
#include "app.h"
#include "skin/skin.h"
#include "ui/gmplayer.h"
#include "ui/widgets.h"
#include "util/list.h"
#include "util/mem.h"
#include "util/string.h"
#include "wm/ws.h"
#include "wm/wsxdnd.h"

#include "access_mpcontext.h"
#include "config.h"
#include "help_mp.h"
#include "input/input.h"
#include "libaf/equalizer.h"
#include "libavutil/common.h"
#include "libmpcodecs/dec_audio.h"
#include "libmpcodecs/dec_video.h"
#include "libmpcodecs/vd.h"
#include "libmpcodecs/vf.h"
#include "libvo/video_out.h"
#include "libvo/x11_common.h"
#include "mixer.h"
#include "mp_msg.h"
#include "mpcommon.h"
#include "mplayer.h"
#include "path.h"
#include "sub/font_load.h"
#include "sub/sub.h"

#ifdef CONFIG_DVDREAD
#include "stream/stream_dvd.h"
#endif

guiInterface_t guiInfo = {
    .StreamType = STREAMTYPE_DUMMY,
    .Balance    = 50.0f
};

static int initialized;

/* MPlayer -> GUI */

void guiInit(void)
{
    int i;

    mp_msg(MSGT_GPLAYER, MSGL_V, "GUI init.\n");

#ifdef CONFIG_DXR3
    if (!gtkDXR3Device)
        gtkDXR3Device = strdup("/dev/em8300-0");
#endif

    if (stream_cache_size > 0) {
        gtkCacheOn   = 1;
        gtkCacheSize = stream_cache_size;
    } else if (stream_cache_size == 0)
        gtkCacheOn = 0;

    if (autosync && (autosync != gtkAutoSync)) {
        gtkAutoSyncOn = 1;
        gtkAutoSync   = autosync;
    }

#ifdef CONFIG_ASS
    gtkASS.enabled       = ass_enabled;
    gtkASS.use_margins   = ass_use_margins;
    gtkASS.top_margin    = ass_top_margin;
    gtkASS.bottom_margin = ass_bottom_margin;
#endif

    gtkInit();

    // initialize X
    wsXInit(mDisplay);

    // load skin

    skinDirInHome  = get_path("skins");
    skinMPlayerDir = MPLAYER_DATADIR "/skins";

    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[interface] skin directory #1: %s\n", skinDirInHome);
    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[interface] skin directory #2: %s\n", skinMPlayerDir);

    if (!skinName)
        skinName = strdup("default");

    i = skinRead(skinName);

    if (i == -1 && strcmp(skinName, "default") != 0) {
        mp_msg(MSGT_GPLAYER, MSGL_WARN, MSGTR_SKIN_SKINCFG_SelectedSkinNotFound, skinName);

        skinName = strdup("default");
        i = skinRead(skinName);
    }

    switch (i) {
    case -1:
        gmp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_SKIN_SKINCFG_SkinNotFound, skinName);
        mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);

    case -2:
        gmp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_SKIN_SKINCFG_SkinCfgError, skinName);
        mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
    }

    // initialize windows

    mainDrawBuffer = malloc(guiApp.main.Bitmap.ImageSize);

    if (!mainDrawBuffer) {
        gmp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_NEMDB);
        mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
    }

    if (gui_save_pos) {
        if (gui_main_pos_x != -3)
            guiApp.main.x = gui_main_pos_x;
        if (gui_main_pos_y != -3)
            guiApp.main.y = gui_main_pos_y;
        if (gui_sub_pos_x != -3)
            guiApp.sub.x = gui_sub_pos_x;
        if (gui_sub_pos_y != -3)
            guiApp.sub.y = gui_sub_pos_y;
    }

    if (WinID > 0) {
        guiApp.subWindow.Parent = WinID;
        guiApp.sub.x = 0;
        guiApp.sub.y = 0;
    }

    if (guiWinID >= 0)
        guiApp.mainWindow.Parent = guiWinID;

    wsCreateWindow(&guiApp.subWindow, guiApp.sub.x, guiApp.sub.y, guiApp.sub.width, guiApp.sub.height, wsNoBorder, wsShowMouseCursor | wsHandleMouseButton | wsHandleMouseMove, wsShowFrame | wsHideWindow, "MPlayer - Video");
    wsDestroyImage(&guiApp.subWindow);
    wsCreateImage(&guiApp.subWindow, guiApp.sub.Bitmap.Width, guiApp.sub.Bitmap.Height);
    wsXDNDMakeAwareness(&guiApp.subWindow);

    WinID = guiApp.subWindow.WindowID;

    uiMenuInit();
    uiPlaybarInit();

// i=wsHideFrame|wsMaxSize|wsHideWindow;
// if ( guiApp.mainDecoration ) i=wsShowFrame|wsMaxSize|wsHideWindow;
    i = wsShowFrame | wsMaxSize | wsHideWindow;
    wsCreateWindow(&guiApp.mainWindow, guiApp.main.x, guiApp.main.y, guiApp.main.width, guiApp.main.height, wsNoBorder, wsShowMouseCursor | wsHandleMouseButton | wsHandleMouseMove, i, "MPlayer");
    wsSetShape(&guiApp.mainWindow, guiApp.main.Mask.Image);
    wsXDNDMakeAwareness(&guiApp.mainWindow);

    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[interface] screen depth: %d\n", wsDepthOnScreen);
    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[interface] mainWindow ID: 0x%x\n", (int)guiApp.mainWindow.WindowID);
    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[interface] subWindow ID: 0x%x\n", (int)guiApp.subWindow.WindowID);

    guiApp.mainWindow.ReDraw       = (void *)uiMainDraw;
    guiApp.mainWindow.MouseHandler = uiMainMouseHandle;
    guiApp.mainWindow.KeyHandler   = uiMainKeyHandle;
    guiApp.mainWindow.DandDHandler = uiDandDHandler;

    guiApp.subWindow.ReDraw       = (void *)uiSubDraw;
    guiApp.subWindow.MouseHandler = uiSubMouseHandle;
    guiApp.subWindow.KeyHandler   = uiMainKeyHandle;
    guiApp.subWindow.DandDHandler = uiDandDHandler;

    wsSetBackgroundRGB(&guiApp.subWindow, guiApp.sub.R, guiApp.sub.G, guiApp.sub.B);
    wsClearWindow(guiApp.subWindow);

    if (guiApp.sub.Bitmap.Image)
        wsConvert(&guiApp.subWindow, guiApp.sub.Bitmap.Image);

    btnModify(evSetVolume, guiInfo.Volume);
    btnModify(evSetBalance, guiInfo.Balance);
    btnModify(evSetMoviePosition, guiInfo.Position);

    wsSetIcon(wsDisplay, guiApp.mainWindow.WindowID, &guiIcon);
    wsSetIcon(wsDisplay, guiApp.subWindow.WindowID, &guiIcon);

    if (!guiApp.mainDecoration)
        wsWindowDecoration(&guiApp.mainWindow, 0);

    wsVisibleWindow(&guiApp.mainWindow, wsShowWindow);

    if (gtkShowVideoWindow) {
        wsVisibleWindow(&guiApp.subWindow, wsShowWindow);

        {
            XEvent xev;

            do
                XNextEvent(wsDisplay, &xev);
            while (xev.type != MapNotify || xev.xmap.event != guiApp.subWindow.WindowID);

            guiApp.subWindow.Mapped = wsMapped;
            guiInfo.VideoWindow     = True;
        }

        if (gtkLoadFullscreen)
            uiFullScreen();
    } else
        wsSetBackgroundRGB(&guiApp.subWindow, 0, 0, 0);

    if (gtkLoadFullscreen)
        btnSet(evFullScreen, btnPressed);

    guiInfo.Playing = GUI_STOP;

    uiSubRender = 1;

    if (plCurrent && !filename) {
        uiSetFileName(plCurrent->path, plCurrent->name, STREAMTYPE_FILE);
        filename = NULL; // don't start playing
    }

    if (subdata)
        setdup(&guiInfo.SubtitleFilename, subdata->filename);

    mplayerLoadFont();

    initialized = 1;
}

void guiDone(void)
{
    if (initialized) {
        uiMainRender = 0;

        if (gui_save_pos) {
            gui_main_pos_x = guiApp.mainWindow.X;
            gui_main_pos_y = guiApp.mainWindow.Y;
            gui_sub_pos_x  = guiApp.sub.x;
            gui_sub_pos_y  = guiApp.sub.y;
        }

#ifdef CONFIG_ASS
        ass_enabled       = gtkASS.enabled;
        ass_use_margins   = gtkASS.use_margins;
        ass_top_margin    = gtkASS.top_margin;
        ass_bottom_margin = gtkASS.bottom_margin;
#endif

        cfg_write();
        wsXDone();
    }

    appFreeStruct();
    free(guiIcon.collection);

    if (gui_conf) {
        m_config_free(gui_conf);
        gui_conf = NULL;
    }

    mp_msg(MSGT_GPLAYER, MSGL_V, "GUI done.\n");
}

static void add_vf(char *str)
{
    void *p;

    if (vf_settings) {
        int i = 0;

        while (vf_settings[i].name) {
            if (!gstrcmp(vf_settings[i++].name, str)) {
                i = -1;
                break;
            }
        }

        if (i != -1) {
            p = realloc(vf_settings, (i + 2) * sizeof(m_obj_settings_t));

            if (!p)
                return;

            vf_settings = p;
            vf_settings[i].name     = strdup(str);
            vf_settings[i].attribs  = NULL;
            vf_settings[i + 1].name = NULL;
        }
    } else {
        vf_settings = malloc(2 * sizeof(m_obj_settings_t));
        vf_settings[0].name    = strdup(str);
        vf_settings[0].attribs = NULL;
        vf_settings[1].name    = NULL;
    }

    mp_msg(MSGT_GPLAYER, MSGL_INFO, MSGTR_AddingVideoFilter, str);
}

int gui(int what, void *data)
{
    mixer_t *mixer = NULL;
    stream_t *stream;
#ifdef CONFIG_DVDREAD
    dvd_priv_t *dvd;
#endif
    plItem *next;
    int state;

    if (guiInfo.mpcontext)
        mixer = mpctx_get_mixer(guiInfo.mpcontext);

    switch (what) {
    case GUI_SET_CONTEXT:
        guiInfo.mpcontext = data;
        break;

    case GUI_SET_STATE:

        switch ((int)data) {
        case GUI_STOP:
        case GUI_PLAY:
// if ( !gtkShowVideoWindow ) wsVisibleWindow( &guiApp.subWindow,wsHideWindow );
        case GUI_PAUSE:
            guiInfo.Playing = (int)data;
            break;
        }

        uiState();
        break;

    case GUI_HANDLE_EVENTS:
        if (!guiInfo.Playing || !guiInfo.VideoWindow)
            wsHandleEvents();
        wsAutohideCursor();
        gtkEventHandling();
        break;

    case GUI_RUN_COMMAND:

        mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[interface] GUI_RUN_COMMAND: %d\n", (int)data);

        switch ((int)data) {
        case MP_CMD_VO_FULLSCREEN:
            uiEventHandling(evFullScreen, 0);
            break;

        case MP_CMD_PLAY_TREE_STEP:
            uiEventHandling(evNext, 0);
            break;

        case -MP_CMD_PLAY_TREE_STEP:
            uiEventHandling(evPrev, 0);
            break;

        case MP_CMD_STOP:
            uiEventHandling(evStop, 0);
            break;

        case MP_CMD_QUIT:
            uiEventHandling(evExit, 0);
            break;
        }

        break;

    case GUI_PREPARE:

        wsVisibleMouse(&guiApp.subWindow, wsHideMouseCursor);

        if (guiInfo.NewPlay == GUI_FILE_NEW) {
            dvd_title = 0;
            audio_id  = -1;
            video_id  = -1;
            dvdsub_id = -1;
            vobsub_id = -1;

            stream_cache_size = -1;
            autosync  = 0;
            force_fps = 0;
        }

        switch (guiInfo.StreamType) {
        case STREAMTYPE_FILE:
        case STREAMTYPE_STREAM:
            break;

#ifdef CONFIG_VCD
        case STREAMTYPE_VCD:
        {
            char tmp[512];

            sprintf(tmp, "vcd://%d", guiInfo.Track);
            uiSetFileName(NULL, tmp, STREAMTYPE_VCD);
        }
        break;
#endif

#ifdef CONFIG_DVDREAD
        case STREAMTYPE_DVD:
        {
            char tmp[512];

            sprintf(tmp, "dvd://%d", guiInfo.Track);
            uiSetFileName(NULL, tmp, STREAMTYPE_DVD);
        }

            dvd_chapter = guiInfo.Chapter;
            dvd_angle   = guiInfo.Angle;

            break;
#endif
        }

        // video opts

        if (!video_driver_list) {
            int i = 0;

            while (video_out_drivers[i++]) {
                if (video_out_drivers[i - 1]->control(VOCTRL_GUISUPPORT, NULL) == VO_TRUE) {
                    gaddlist(&video_driver_list, (char *)video_out_drivers[i - 1]->info->short_name);
                    break;
                }
            }
        }

        if (!video_driver_list && !video_driver_list[0]) {
            gmp_msg(MSGT_GPLAYER, MSGL_FATAL, MSGTR_IDFGCVD);
            mplayer(MPLAYER_EXIT_GUI, EXIT_ERROR, 0);
        }

        {
            int i = 0;

            guiInfo.VideoWindow = True;

            while (video_out_drivers[i++]) {
                if (video_out_drivers[i - 1]->control(VOCTRL_GUISUPPORT, NULL) == VO_TRUE) {
                    if ((video_driver_list && !gstrcmp(video_driver_list[0], (char *)video_out_drivers[i - 1]->info->short_name)) && (video_out_drivers[i - 1]->control(VOCTRL_GUI_NOWINDOW, NULL) == VO_TRUE)) {
                        guiInfo.VideoWindow = False;
                        break;
                    }
                }
            }
        }

#ifdef CONFIG_DXR3
        if (video_driver_list && !gstrcmp(video_driver_list[0], "dxr3"))
            if (guiInfo.StreamType != STREAMTYPE_DVD && guiInfo.StreamType != STREAMTYPE_VCD)
                if (gtkVfLAVC)
                    add_vf("lavc");
#endif

        if (gtkVfPP)
            add_vf("pp");

        // audio opts

// if ( ao_plugin_cfg.plugin_list ) { free( ao_plugin_cfg.plugin_list ); ao_plugin_cfg.plugin_list=NULL; }
        if (gtkAONorm)
            greplace(&af_cfg.list, "volnorm", "volnorm");

        if (gtkEnableAudioEqualizer)
            greplace(&af_cfg.list, "equalizer", "equalizer");

        if (gtkAOExtraStereo) {
            char *name;

            name = malloc(12 + 20 + 1);
            snprintf(name, 12 + 20, "extrastereo=%f", gtkAOExtraStereoMul);
            name[12 + 20] = 0;
            greplace(&af_cfg.list, "extrastereo", name);
            free(name);
        }

#ifdef CONFIG_OSS_AUDIO
        if (audio_driver_list && !gstrncmp(audio_driver_list[0], "oss", 3)) {
            char *tmp;

            mixer_device  = gtkAOOSSMixer;
            mixer_channel = gtkAOOSSMixerChannel;

            if (gtkAOOSSDevice) {
                tmp = calloc(1, strlen(gtkAOOSSDevice) + 7);
                sprintf(tmp, "oss:%s", gtkAOOSSDevice);
            } else
                tmp = strdup("oss");

            gaddlist(&audio_driver_list, tmp);
            free(tmp);
        }
#endif

#ifdef CONFIG_ALSA
        if (audio_driver_list && !gstrncmp(audio_driver_list[0], "alsa", 4)) {
            char *tmp;

            mixer_device  = gtkAOALSAMixer;
            mixer_channel = gtkAOALSAMixerChannel;

            if (gtkAOALSADevice) {
                tmp = calloc(1, strlen(gtkAOALSADevice) + 14);
                sprintf(tmp, "alsa:device=%s", gtkAOALSADevice);
            } else
                tmp = strdup("alsa");

            gaddlist(&audio_driver_list, tmp);
            free(tmp);
        }
#endif

#ifdef CONFIG_SDL
        if (audio_driver_list && !gstrncmp(audio_driver_list[0], "sdl", 3)) {
            char *tmp;

            if (gtkAOSDLDriver) {
                tmp = calloc(1, strlen(gtkAOSDLDriver) + 10);
                sprintf(tmp, "sdl:%s", gtkAOSDLDriver);
            } else
                tmp = strdup("sdl");

            gaddlist(&audio_driver_list, tmp);
            free(tmp);
        }
#endif

#ifdef CONFIG_ESD
        if (audio_driver_list && !gstrncmp(audio_driver_list[0], "esd", 3)) {
            char *tmp;

            if (gtkAOESDDevice) {
                tmp = calloc(1, strlen(gtkAOESDDevice) + 10);
                sprintf(tmp, "esd:%s", gtkAOESDDevice);
            } else
                tmp = strdup("esd");

            gaddlist(&audio_driver_list, tmp);
            free(tmp);
        }
#endif

        // subtitle

// subdata->filename=gstrdup( guiInfo.SubtitleFilename );
        stream_dump_type = 0;

        if (gtkSubDumpMPSub)
            stream_dump_type = 4;

        if (gtkSubDumpSrt)
            stream_dump_type = 6;

        gtkSubDumpMPSub = gtkSubDumpSrt = 0;
        mplayerLoadFont();

        // misc

        if (gtkCacheOn)
            stream_cache_size = gtkCacheSize;

        if (gtkAutoSyncOn)
            autosync = gtkAutoSync;

        if (guiInfo.AudioFilename)
            audio_stream = gstrdup(guiInfo.AudioFilename);
        else if (guiInfo.NewPlay == GUI_FILE_NEW)
            nfree(audio_stream);

// audio_stream = NULL;

        guiInfo.NewPlay = 0;

#ifdef CONFIG_ASS
        ass_enabled       = gtkASS.enabled;
        ass_use_margins   = gtkASS.use_margins;
        ass_top_margin    = gtkASS.top_margin;
        ass_bottom_margin = gtkASS.bottom_margin;
#endif

        break;

    case GUI_SET_STREAM:

        stream = data;
        guiInfo.StreamType = stream->type;

        switch (guiInfo.StreamType) {
#ifdef CONFIG_VCD
        case STREAMTYPE_VCD:
            guiInfo.Tracks = 0;
            stream_control(stream, STREAM_CTRL_GET_NUM_CHAPTERS, &guiInfo.Tracks);
            break;
#endif

#ifdef CONFIG_DVDREAD
        case STREAMTYPE_DVD:
            dvd = stream->priv;
            guiInfo.Tracks       = dvd->vmg_file->tt_srpt->nr_of_srpts;
            guiInfo.Chapters     = dvd->vmg_file->tt_srpt->title[dvd_title].nr_of_ptts;
            guiInfo.Angles       = dvd->vmg_file->tt_srpt->title[dvd_title].nr_of_angles;
            guiInfo.AudioStreams = dvd->nr_of_channels;
            memcpy(guiInfo.AudioStream, dvd->audio_streams, sizeof(dvd->audio_streams));
            guiInfo.Subtitles = dvd->nr_of_subtitles;
            memcpy(guiInfo.Subtitle, dvd->subtitles, sizeof(dvd->subtitles));
            guiInfo.Track   = dvd_title + 1;
            guiInfo.Chapter = dvd_chapter + 1;
            guiInfo.Angle   = dvd_angle + 1;
            break;
#endif

        default:
            break;
        }

        break;

    case GUI_SET_AFILTER:
        guiInfo.afilter = data;
        break;

    case GUI_SET_VIDEO:

        // video

        guiInfo.sh_video = data;

        state = (guiInfo.StreamType == STREAMTYPE_STREAM ? btnDisabled : btnReleased);
        btnSet(evForward10sec, state);
        btnSet(evBackward10sec, state);
        btnSet(evForward1min, state);
        btnSet(evBackward1min, state);
        btnSet(evForward10min, state);
        btnSet(evBackward10min, state);
        btnSet(evSetMoviePosition, state);

#ifdef CONFIG_DXR3
        if (video_driver_list && !gstrcmp(video_driver_list[0], "dxr3") && (((demuxer_t *)mpctx_get_demuxer(guiInfo.mpcontext))->file_format != DEMUXER_TYPE_MPEG_PS) && !gtkVfLAVC) {
            gtkMessageBox(GTK_MB_FATAL, MSGTR_NEEDLAVC);
            return False;
        }
#endif

        break;

    case GUI_SET_AUDIO:

        guiInfo.AudioChannels = data ? ((sh_audio_t *)data)->channels : 0;

        if (data && !guiInfo.sh_video)
            guiInfo.VideoWindow = False;

        gui(GUI_SET_MIXER, 0);

        if (gtkEnableAudioEqualizer) {
            equalizer_t eq;
            unsigned int i, j;

            for (i = 0; i < FF_ARRAY_ELEMS(gtkEquChannels); i++) {
                for (j = 0; j < FF_ARRAY_ELEMS(*gtkEquChannels); j++) {
                    eq.channel = i;
                    eq.band    = j;
                    eq.gain    = gtkEquChannels[i][j];
                    mplayer(MPLAYER_SET_EQUALIZER, 0, &eq);
                }
            }
        }

        // These must be done here (in the last call from MPlayer before
        // playback starts) and not in GUI_SETUP_VIDEO_WINDOW, because...

        // ...without video there will be no call to GUI_SETUP_VIDEO_WINDOW
        if (!guiInfo.VideoWindow) {
            wsVisibleWindow(&guiApp.subWindow, wsHideWindow);
            btnSet(evFullScreen, (gtkLoadFullscreen ? btnPressed : btnReleased));
        }

        // ...option variable fullscreen determines whether MPlayer will handle
        //    the window given by WinID as fullscreen window (and will do aspect
        //    scaling then) or not - quite rubbish
        fullscreen = gtkLoadFullscreen;

        break;

    case GUI_SET_MIXER:
        if (mixer) {
            float l, r;
            static float last_balance = -1;

            mixer_getvolume(mixer, &l, &r);

            guiInfo.Volume = FFMAX(l, r);
            btnModify(evSetVolume, guiInfo.Volume);

            if (guiInfo.Balance != last_balance) {
                if (guiInfo.Volume)
                    guiInfo.Balance = ((r - l) / guiInfo.Volume + 1.0) * 50.0;
                else
                    guiInfo.Balance = 50.0f;

                last_balance = guiInfo.Balance;
                btnModify(evSetBalance, guiInfo.Balance);
            }
        }
        break;

    case GUI_REDRAW:
        uiEventHandling(ivRedraw, 0);
        break;

    case GUI_SETUP_VIDEO_WINDOW:

        guiInfo.VideoWidth  = vo_dwidth;
        guiInfo.VideoHeight = vo_dheight;

        if (!guiApp.subWindow.isFullScreen || !guiApp.subWindow.Mapped) {
            if (!guiApp.subWindow.isFullScreen)
                wsResizeWindow(&guiApp.subWindow, guiInfo.VideoWidth, guiInfo.VideoHeight);

            wsMoveWindow(&guiApp.subWindow, False, guiApp.sub.x, guiApp.sub.y);

            if (!guiApp.subWindow.Mapped)
                wsVisibleWindow(&guiApp.subWindow, wsShowWindow);
        }

        if (gtkLoadFullscreen ^ guiApp.subWindow.isFullScreen)
            uiEventHandling(evFullScreen, 0);

        if (guiWinID >= 0)
            wsMoveWindow(&guiApp.mainWindow, True, 0, guiInfo.VideoHeight);

        break;

    case GUI_HANDLE_X_EVENT:
        wsEvents(wsDisplay, data);
        gtkEventHandling();
        break;

    case GUI_END_FILE:

        uiEventHandling(ivRedraw, 1);

        guiInfo.sh_video = NULL;

        if (!uiGotoTheNext && guiInfo.Playing) {
            uiGotoTheNext = 1;
            break;
        }

        if (guiInfo.Playing && (next = listSet(gtkGetNextPlItem, NULL)) && (plLastPlayed != next)) {
            plLastPlayed = next;
            uiSetFileName(next->path, next->name, STREAMTYPE_FILE);
            guiInfo.NewPlay = GUI_FILE_NEW;
            guiInfo.Track++;
        } else {
            if (guiInfo.NewPlay == GUI_FILE_NEW)
                break;

            filename = NULL;

            guiInfo.ElapsedTime   = 0;
            guiInfo.Position      = 0;
            guiInfo.AudioChannels = 0;

#ifdef CONFIG_DVDREAD
            guiInfo.Track   = 1;
            guiInfo.Chapter = 1;
            guiInfo.Angle   = 1;
#endif

            if (gtkShowVideoWindow) {
                guiInfo.VideoWindow = True;
                guiInfo.VideoWidth  = guiApp.sub.width;
                guiInfo.VideoHeight = guiApp.sub.height;

                if (!guiApp.subWindow.isFullScreen) {
                    wsResizeWindow(&guiApp.subWindow, guiInfo.VideoWidth, guiInfo.VideoHeight);
                    wsMoveWindow(&guiApp.subWindow, False, guiApp.sub.x, guiApp.sub.y);
                }

                if (!guiApp.subWindow.Mapped)
                    wsVisibleWindow(&guiApp.subWindow, wsShowWindow);

                if (gtkLoadFullscreen ^ guiApp.subWindow.isFullScreen)
                    uiEventHandling(evFullScreen, 0);
            } else {
                wsVisibleWindow(&guiApp.subWindow, wsHideWindow);
                guiInfo.VideoWindow = False;
                btnSet(evFullScreen, (gtkLoadFullscreen ? btnPressed : btnReleased));
            }

            gui(GUI_SET_STATE, (void *)GUI_STOP);

            wsHandleEvents();
            uiSubRender = 1;
            wsSetBackgroundRGB(&guiApp.subWindow, guiApp.sub.R, guiApp.sub.G, guiApp.sub.B);
            wsClearWindow(guiApp.subWindow);
            wsPostRedisplay(&guiApp.subWindow);
            wsVisibleMouse(&guiApp.subWindow, wsShowMouseCursor);
        }

        break;
    }

    return True;
}

// This function adds/inserts one file into the gui playlist.
static int import_file_into_gui(char *temp, int insert)
{
    char *filename, *pathname;
    plItem *item;

    filename = strdup(mp_basename(temp));
    pathname = strdup(temp);

    if (strlen(pathname) - strlen(filename) > 0)
        pathname[strlen(pathname) - strlen(filename) - 1] = 0;                                            // we have some path, so remove / at end
    else
        pathname[strlen(pathname) - strlen(filename)] = 0;

    mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[interface] playtree, add: %s/%s\n", pathname, filename);

    item = calloc(1, sizeof(plItem));

    if (!item)
        return 0;

    item->name = filename;
    item->path = pathname;

    if (insert)
        listSet(gtkInsertPlItem, item);           // inserts the item after current, and makes current=item
    else
        listSet(gtkAddPlItem, item);

    return 1;
}

// This function imports the initial playtree (based on cmd-line files)
// into the gui playlist by either:
// - overwriting gui pl (enqueue=0)
// - appending it to gui pl (enqueue=1)
int guiPlaylistInitialize(play_tree_t *my_playtree, m_config_t *config, int enqueue)
{
    play_tree_iter_t *my_pt_iter = NULL;
    int result = 0;

    if (!enqueue)
        listSet(gtkDelPl, NULL);             // delete playlist before "appending"

    if ((my_pt_iter = pt_iter_create(&my_playtree, config))) {
        while ((filename = pt_iter_get_next_file(my_pt_iter)) != NULL)
            // add it to end of list
            if (import_file_into_gui(filename, 0))
                result = 1;
    }

    uiCurr();   // update filename
    uiGotoTheNext = 1;

    if (enqueue)
        filename = NULL;            // don't start playing

    return result;
}

// This function imports and inserts an playtree, that is created "on the fly",
// for example by parsing some MOV-Reference-File; or by loading an playlist
// with "File Open". (The latter, actually, isn't allowed in MPlayer and thus
// not working which is why this function won't get called for that reason.)
// The file which contained the playlist is thereby replaced with it's contents.
int guiPlaylistAdd(play_tree_t *my_playtree, m_config_t *config)
{
    play_tree_iter_t *my_pt_iter = NULL;
    int result = 0;
    plItem *save;

    save = (plItem *)listSet(gtkGetCurrPlItem, NULL);    // save current item

    if ((my_pt_iter = pt_iter_create(&my_playtree, config))) {
        while ((filename = pt_iter_get_next_file(my_pt_iter)) != NULL)
            // insert it into the list and set plCurrent=new item
            if (import_file_into_gui(filename, 1))
                result = 1;

        pt_iter_destroy(&my_pt_iter);
    }

    if (save)
        listSet(gtkSetCurrPlItem, save);
    else
        listSet(gtkSetCurrPlItem, plList);    // go to head, if plList was empty before

    if (save && result)
        listSet(gtkDelCurrPlItem, NULL);

    uiCurr();   // update filename

    return result;
}

/* GUI -> MPlayer */

void mplayer(int what, float value, void *data)
{
    equalizer_t *eq = (equalizer_t *)data;

    switch (what) {
        // subtitle

#ifndef CONFIG_FREETYPE
    case MPLAYER_SET_FONT_FACTOR:
        font_factor = value;
        mplayerLoadFont();
        break;
#else
    case MPLAYER_SET_FONT_OUTLINE:
        subtitle_font_thickness = (8.0f / 100.0f) * value;
        mplayerLoadFont();
        break;

    case MPLAYER_SET_FONT_BLUR:
        subtitle_font_radius = (8.0f / 100.0f) * value;
        mplayerLoadFont();
        break;

    case MPLAYER_SET_FONT_TEXTSCALE:
        text_font_scale_factor = value;
        mplayerLoadFont();
        break;

    case MPLAYER_SET_FONT_OSDSCALE:
        osd_font_scale_factor = value;
        mplayerLoadFont();
        break;

    case MPLAYER_SET_FONT_ENCODING:
        nfree(subtitle_font_encoding);
        subtitle_font_encoding = gstrdup((char *)data);
        mplayerLoadFont();
        break;

    case MPLAYER_SET_FONT_AUTOSCALE:
        subtitle_autoscale = (int)value;
        mplayerLoadFont();
        break;
#endif

#ifdef CONFIG_ICONV
    case MPLAYER_SET_SUB_ENCODING:
        nfree(sub_cp);
        sub_cp = gstrdup((char *)data);
        break;
#endif

    case MPLAYER_SET_EXTRA_STEREO:
        gtkAOExtraStereoMul = value;
        if (guiInfo.afilter)
            af_control_any_rev(guiInfo.afilter, AF_CONTROL_ES_MUL | AF_CONTROL_SET, &gtkAOExtraStereoMul);
        break;

    case MPLAYER_SET_PANSCAN:
    {
        mp_cmd_t *mp_cmd;

        mp_cmd       = calloc(1, sizeof(*mp_cmd));
        mp_cmd->id   = MP_CMD_PANSCAN;
        mp_cmd->name = strdup("panscan");
        mp_cmd->args[0].v.f = value;
        mp_cmd->args[1].v.i = 1;
        mp_input_queue_cmd(mp_cmd);
    }
    break;

    case MPLAYER_SET_AUTO_QUALITY:
        auto_quality = (int)value;
        break;

    // set equalizers

    case MPLAYER_SET_CONTRAST:
        if (guiInfo.sh_video)
            set_video_colors(guiInfo.sh_video, "contrast", (int)value);
        break;

    case MPLAYER_SET_BRIGHTNESS:
        if (guiInfo.sh_video)
            set_video_colors(guiInfo.sh_video, "brightness", (int)value);
        break;

    case MPLAYER_SET_HUE:
        if (guiInfo.sh_video)
            set_video_colors(guiInfo.sh_video, "hue", (int)value);
        break;

    case MPLAYER_SET_SATURATION:
        if (guiInfo.sh_video)
            set_video_colors(guiInfo.sh_video, "saturation", (int)value);
        break;

    case MPLAYER_SET_EQUALIZER:
    {
        af_control_ext_t tmp;

        if (eq) {
            gtkEquChannels[eq->channel][eq->band] = eq->gain;
            tmp.ch  = eq->channel;
            tmp.arg = gtkEquChannels[eq->channel];

            if (guiInfo.afilter)
                af_control_any_rev(guiInfo.afilter, AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_SET, &tmp);
        } else {
            unsigned int i;

            memset(gtkEquChannels, 0, sizeof(gtkEquChannels));

            if (guiInfo.afilter) {
                for (i = 0; i < FF_ARRAY_ELEMS(gtkEquChannels); i++) {
                    tmp.ch  = i;
                    tmp.arg = gtkEquChannels[i];
                    af_control_any_rev(guiInfo.afilter, AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_SET, &tmp);
                }
            }
        }

        break;
    }

    case MPLAYER_EXIT_GUI:
        exit_player_with_rc((enum exit_reason)value, (enum exit_reason)value >= EXIT_ERROR);
        break;
    }
}

void mplayerLoadFont(void)
{
#ifdef CONFIG_FREETYPE
    load_font_ft(vo_image_width, vo_image_height, &vo_font, font_name, osd_font_scale_factor);
#else
    if (vo_font) {
        int i;

        free(vo_font->name);
        free(vo_font->fpath);

        for (i = 0; i < 16; i++) {
            if (vo_font->pic_a[i]) {
                free(vo_font->pic_a[i]->bmp);
                free(vo_font->pic_a[i]->pal);
            }
        }

        for (i = 0; i < 16; i++) {
            if (vo_font->pic_b[i]) {
                free(vo_font->pic_b[i]->bmp);
                free(vo_font->pic_b[i]->pal);
            }
        }

        free(vo_font);
        vo_font = NULL;
    }

    if (font_name) {
        vo_font = read_font_desc(font_name, font_factor, 0);

        if (!vo_font)
            gmp_msg(MSGT_GPLAYER, MSGL_ERR, MSGTR_CantLoadFont, font_name);
    } else {
        font_name = gstrdup(get_path("font/font.desc"));
        vo_font   = read_font_desc(font_name, font_factor, 0);

        if (!vo_font) {
            nfree(font_name);
            font_name = gstrdup(MPLAYER_DATADIR "/font/font.desc");
            vo_font   = read_font_desc(font_name, font_factor, 0);
        }
    }
#endif
}

void mplayerLoadSubtitle(const char *name)
{
    if (guiInfo.Playing == 0)
        return;

    if (subdata) {
        mp_msg(MSGT_GPLAYER, MSGL_INFO, MSGTR_DeletingSubtitles);

        sub_free(subdata);
        subdata = NULL;
        vo_sub  = NULL;

        if (vo_osd_list) {
            int len;
            mp_osd_obj_t *osd;

            osd = vo_osd_list;

            while (osd) {
                if (osd->type == OSDTYPE_SUBTITLE)
                    break;

                osd = osd->next;
            }

            if (osd && (osd->flags & OSDFLAG_VISIBLE)) {
                len = osd->stride * (osd->bbox.y2 - osd->bbox.y1);
                memset(osd->bitmap_buffer, 0, len);
                memset(osd->alpha_buffer, 0, len);
            }
        }
    }

    if (name) {
        mp_msg(MSGT_GPLAYER, MSGL_INFO, MSGTR_LoadingSubtitles, name);

        subdata = sub_read_file(name, (guiInfo.sh_video ? guiInfo.sh_video->fps : 0));

        if (!subdata)
            gmp_msg(MSGT_GPLAYER, MSGL_ERR, MSGTR_CantLoadSub, name);

        sub_name    = (malloc(2 * sizeof(char *))); // when mplayer will be restarted
        sub_name[0] = strdup(name);                 // sub_name[0] will be read
        sub_name[1] = NULL;
    }

    update_set_of_subtitles();
}

// NOTE TO MYSELF: This function is nonsense.
//                 MPlayer should pass messages to the GUI
//                 which must decide then which message has
//                 to be shown (MSGL_FATAL, for example).
//                 But with this function it is at least
//                 possible to show GUI's very critical or
//                 abort messages.
void gmp_msg(int mod, int lev, const char *format, ...)
{
    char msg[512];
    va_list va;

    va_start(va, format);
    vsnprintf(msg, sizeof(msg), format, va);
    va_end(va);

    mp_msg(mod, lev, msg);

    if (mp_msg_test(mod, lev))
        gtkMessageBox(GTK_MB_FATAL, msg);
}