/* * Copyright 2022 Olaf Wintermann * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "window.h" #include "main.h" #include "player.h" #include "playlist.h" #include "xdnd.h" #include "settings.h" #include "Fsb.h" #include "Sidebar.h" static MainWindow *main_window; static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *args, int nargs); static void FileOpenCB(Widget w, void *udata, void *cdata); static void FileQuitCB(Widget w, void *udata, void *cdata); static void PlayRepeatCB(Widget w, void *udata, void *cdata); static void PlayRepeatListCB(Widget w, void *udata, void *cdata); static void PlayAutoPlayCB(Widget w, void *udata, void *cdata); static void PlayRandomCB(Widget w, void *udata, void *cdata); static void ViewFullscreenCB(Widget w, void *udata, void *cdata); static void ViewSidebarCB(Widget w, void *udata, void *cdata); static void ViewAdjustWindowSizeCB(Widget w, void *udata, void *cdata); static void PrefSingleInstanceCB(Widget w, void *udata, void *cdata); static void WindowRealized(MainWindow *win); static int blank_cursor_init = 0; static Pixmap blank_cursor_pixmap; static Cursor blank_cursor; static void init_blank_cursor(Widget w) { char data = 0; XColor c; blank_cursor_pixmap = XCreateBitmapFromData(XtDisplay(w), XtWindow(w), &data, 1, 1); if(!blank_cursor_pixmap) return; blank_cursor = XCreatePixmapCursor(XtDisplay(w), blank_cursor_pixmap, blank_cursor_pixmap, &c, &c, 0, 0); XFreePixmap(XtDisplay(w), blank_cursor_pixmap); blank_cursor_init = 1; } static void window_close_handler(Widget window, void *udata, void *cdata) { FileQuitCB(window, NULL, NULL); } static unsigned int keycodeF; static void windowKeyEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { MainWindow *win = data; if(win->fullscreen && event->xkey.keycode == keycodeF) { WindowFullscreen(main_window, FALSE); *dispatch = FALSE; } } static int main_window_is_realized = 0; static void resizeEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { if(!main_window_is_realized) { if(XtIsRealized(widget)) { main_window_is_realized = 1; WindowRealized(data); } } WindowAdjustAspectRatio(data); } static void WindowRealized(MainWindow *win) { char *open_file = GetOpenFileArg(); if(open_file) { PlayListAddFile(win, open_file); PlayListPlayNext(win, true); CleanOpenFileArg(); } if(!blank_cursor_init) { init_blank_cursor(win->player_widget); } XdndEnable(win->window); } static void playerWidgetInputCB(Widget widget, XtPointer u, XtPointer c) { MainWindow *win = u; XmDrawingAreaCallbackStruct *cb = c; if(win->player && win->player->isactive) { PlayerHandleInput(win, win->player, cb); } } static void windowGrabButton(MainWindow *win) { //printf("grab\n"); XtGrabButton( win->player_widget, AnyButton, AnyModifier, True, ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | EnterWindowMask | LeaveWindowMask, GrabModeAsync, GrabModeAsync, None, None); win->buttongrab = True; } static void playerEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { MainWindow *win = data; int etype = event->type; ///* if(etype == EnterNotify) { //printf("enter\n"); windowGrabButton(win); return; } if(etype == LeaveNotify) { //printf("leave\n"); //XtUngrabButton(win->player_widget, AnyButton, AnyModifier); //win->buttongrab = False; return; } int pass = 0; if(etype == ButtonPress || etype == ButtonRelease || etype == KeyPress || etype == KeyRelease) { //printf("button press\n"); pass = 1; } if(!win->player || win->player->window == 0) return; WindowHandlePlayerEvent(win, event); if(pass) { // redirect key events to the player window //printf("redirect\n"); event->xkey.window = win->player->window; XSendEvent( XtDisplay(win->player_widget), win->player->window, True, 0, event); } } #define IGNORE_MOTION_THRESHOLD_MS 1000 #define MOTION_POS_THRESHOLD_PIX 5 #define OSD_BOTTOM_THRESHOLD 0.09 #define DOUBLE_CLICK_TIME_MS 500 void WindowHandlePlayerEvent(MainWindow *win, XEvent *event) { // event handler for intercepted player mouse events // win->player is not NULL int etype = event->type; if(etype == MotionNotify) { Time cur_motion_time = event->xmotion.time; if(win->player) { win->motion_playback_time = win->player->playback_time; } int x = event->xmotion.x_root; int y = event->xmotion.y_root; if(win->cursorhidden && cur_motion_time - win->player_event_time < IGNORE_MOTION_THRESHOLD_MS) { int diff_x = abs(x - win->mouse_x); int diff_y = abs(y - win->mouse_y); if(diff_x > MOTION_POS_THRESHOLD_PIX || diff_y > MOTION_POS_THRESHOLD_PIX) { WindowShowPlayerCursor(win); } } else { win->mouse_x = x; win->mouse_y = y; } win->player_event_time = cur_motion_time; win->motion_playback_time = win->player->playback_time; if(win->pwbuttonpressed) { Display *dp = XtDisplay(win->window); XtUngrabPointer(win->player_widget, CurrentTime); XEvent xev; memset(&xev, 0, sizeof(xev)); xev.type = ClientMessage; xev.xclient.message_type = XInternAtom(dp, "_NET_WM_MOVERESIZE", False); xev.xclient.window = XtWindow(win->window); xev.xclient.format = 32; xev.xclient.data.l[0] = x; xev.xclient.data.l[1] = y; xev.xclient.data.l[2] = 8; // _NET_WM_MOVERESIZE_MOVE xev.xclient.data.l[3] = 1; // button1 xev.xclient.data.l[4] = 1; // source indication XSendEvent(dp, DefaultRootWindow(dp), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); win->pwbuttonpressed = FALSE; } } else if(etype == ButtonPress) { Time t = event->xbutton.time; int yi = win->player_widget->core.height - event->xbutton.y; if((float)yi/(float)win->player_widget->core.height < OSD_BOTTOM_THRESHOLD) { win->button_press_time = 0; } else { if(t - win->button_press_time < DOUBLE_CLICK_TIME_MS) { // double click WindowFullscreen(main_window, !win->fullscreen); win->button_press_time = 0; } else { win->button_press_time = t; } win->pwbuttonpressed = 1; } } else if(etype == ButtonRelease) { win->player_event_time = event->xbutton.time; win->pwbuttonpressed = FALSE; } } MainWindow* WindowCreate(Display *display) { Arg args[32]; int n; MainWindow *window = malloc(sizeof(MainWindow)); memset(window, 0, sizeof(MainWindow)); main_window = window; // toplevel window n = 0; XtSetArg(args[n], XmNtitle, APP_NAME); n++; window->window = XtAppCreateShell( APP_NAME, APP_CLASS, applicationShellWidgetClass, //vendorShellWidgetClass, display, args, n); // close handler Atom wm_delete_window; wm_delete_window = XmInternAtom( display, "WM_DELETE_WINDOW", 0); XmAddWMProtocolCallback( window->window, wm_delete_window, window_close_handler, window); // resize handler XtAddEventHandler(window->window, StructureNotifyMask, False, resizeEH, window); n = 0; XtSetArg(args[n], XmNwidth, 360); n++; XtSetArg(args[n], XmNheight, 220); n++; XtSetArg(args[n], XmNshadowThickness, 0); n++; Widget container = XmCreateForm(window->window, "form", args, n); XtManageChild(container); XtAddEventHandler(container, KeyPressMask, FALSE, windowKeyEH, window); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; WindowCreateMenu(window, container, args, n); n = 0; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNtopWidget, window->menubar); n++; XtSetArg(args[n], XmNwidth, 300); n++; XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); n++; XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmAS_NEEDED); n++; XtSetArg(args[n], XmNspacing, 0); n++; XtSetArg(args[n], XmNshadowThickness, 0); n++; window->sidebar_scrolledwindow = XmCreateScrolledWindow(container, "sw_sidebar", args, n); window->sidebar = CreateSidebar(window->sidebar_scrolledwindow, "sidebar", args, 0); SidebarSetWindow(window->sidebar, window); XtManageChild(window->sidebar); //XtManageChild(window->sidebar); n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightWidget, window->sidebar); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNtopWidget, window->menubar); n++; XtSetArg(args[n], XmNbackground, BlackPixelOfScreen(XtScreen(window->window))); n++; window->player_widget = XmCreateDrawingArea(container, "player", args, n); XtManageChild(window->player_widget); XtAddCallback(window->player_widget, XmNinputCallback, playerWidgetInputCB, window); XmProcessTraversal(window->player_widget, XmTRAVERSE_CURRENT); XtAddEventHandler(window->player_widget, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask | EnterWindowMask | KeyPressMask | KeyReleaseMask | LeaveWindowMask, FALSE, playerEH, window); // get F keycode keycodeF = XKeysymToKeycode(XtDisplay(window->window), XStringToKeysym("F")); PlayListInit(window); window->adjustWindowSize = true; // auto adjust window size by default return window; } MainWindow* GetMainWindow(void) { return main_window; } void WindowShow(MainWindow *win) { XtRealizeWidget(win->window); } /* * Creates a XmPushButton menu item */ static Widget createMenuItem( Widget menu, char *name, char *label, char mnemonic, const char *accelerator, char *accelerator_text, XtCallbackProc callback, void *cbData) { Arg args[16]; int n = 0; XmString s1 = XmStringCreateSimple(label); XtSetArg(args[n], XmNlabelString, s1); n++; XtSetArg(args[n], XmNmnemonic, mnemonic); n++; XmString at = NULL; if(accelerator && accelerator_text) { at = XmStringCreateSimple(accelerator_text); XtSetArg(args[n], XmNaccelerator, accelerator); n++; XtSetArg(args[n], XmNacceleratorText, at); n++; } Widget menuItem = XmCreatePushButtonGadget(menu, name, args, n); XtManageChild(menuItem); XmStringFree(s1); if(at) XmStringFree(at); if(callback) { XtAddCallback(menuItem, XmNactivateCallback, (XtCallbackProc)callback, cbData); } return menuItem; } /* * Creates a XmToggleButton menu item */ static Widget createToggleMenuItem( Widget menu, char *name, char *label, char mnemonic, Boolean defaultValue, const char *accelerator, char *accelerator_text, XtCallbackProc callback, void *cbData) { Arg args[16]; int n = 0; XmString s1 = XmStringCreateSimple(label); XtSetArg(args[n], XmNlabelString, s1); n++; XtSetArg(args[n], XmNmnemonic, mnemonic); n++; XtSetArg(args[n], XmNset, defaultValue); n++; XmString at = NULL; if(accelerator && accelerator_text) { at = XmStringCreateSimple(accelerator_text); XtSetArg(args[n], XmNaccelerator, accelerator); n++; XtSetArg(args[n], XmNacceleratorText, at); n++; } Widget menuItem = XmCreateToggleButtonGadget(menu, name, args, n); XtManageChild(menuItem); XmStringFree(s1); if(at) XmStringFree(at); if(callback) { XtAddCallback(menuItem, XmNvalueChangedCallback, (XtCallbackProc)callback, cbData); } return menuItem; } /* * Creates a menu separator */ static Widget createMenuSeparator(Widget menu) { Widget w = XmCreateSeparator(menu, "separator", NULL, 0); XtManageChild(w); return w; } static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nmbargs) { Widget menubar = XmCreateMenuBar(parent, "menubar", mbargs, nmbargs); XtManageChild(menubar); win->menubar = menubar; Arg args[16]; int n; // menus XmString s = XmStringCreateSimple("File"); XtVaCreateManagedWidget( "menuitem", xmCascadeButtonWidgetClass, menubar, XmNlabelString, s, NULL); XmStringFree(s); Widget fileMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 0, NULL, NULL); s = XmStringCreateSimple("Playback"); XtVaCreateManagedWidget( "menuitem", xmCascadeButtonWidgetClass, menubar, XmNlabelString, s, NULL); XmStringFree(s); Widget playMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 1, NULL, NULL); s = XmStringCreateSimple("View"); Widget viewMenuItem = XtVaCreateManagedWidget( "menuitem", xmCascadeButtonWidgetClass, menubar, XmNlabelString, s, NULL); XmStringFree(s); Widget viewMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 2, NULL, NULL); s = XmStringCreateSimple("Preferences"); Widget prefMenuItem = XtVaCreateManagedWidget( "menuitem", xmCascadeButtonWidgetClass, menubar, XmNlabelString, s, NULL); XmStringFree(s); Widget prefMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 3, NULL, NULL); // file menu createMenuItem(fileMenu, "fileOpen", "Open...", 'O', "CtrlO", "Ctrl+O", FileOpenCB, NULL); createMenuItem(fileMenu, "fileQuit", "Exit", 'E', "CtrlQ", "Ctrl+Q", FileQuitCB, NULL); // play menu win->playRepeatTrackButton = createToggleMenuItem(playMenu, "playRepeatTrack", "Repeat", 'R', False, NULL, NULL, PlayRepeatCB, win); win->playRepeatListButton = createToggleMenuItem(playMenu, "playRepeatList", "Repeat List", 'L', False, NULL, NULL, PlayRepeatListCB, win); win->playRandom = createToggleMenuItem(playMenu, "playRandom", "Random Playback", 'P', False, NULL, NULL, PlayRandomCB, win); win->playAutoPlayButton = createToggleMenuItem(playMenu, "playAutoNext", "Autoplay Folder", 'A', False, NULL, NULL, PlayAutoPlayCB, win); XtVaSetValues(win->playRepeatTrackButton, XmNindicatorType, XmONE_OF_MANY, NULL); XtVaSetValues(win->playRepeatListButton, XmNindicatorType, XmONE_OF_MANY, NULL); XtVaSetValues(win->playAutoPlayButton, XmNindicatorType, XmONE_OF_MANY, NULL); XtVaSetValues(win->playRandom, XmNindicatorType, XmONE_OF_MANY, NULL); // view menu createMenuItem(viewMenu, "viewFullscreen", "Fullscreen", 'F', "F", "F", ViewFullscreenCB, NULL); win->viewSidebarButton = createToggleMenuItem(viewMenu, "viewSidebar", "View Sidebar", 'S', False, NULL, NULL, ViewSidebarCB, win); createMenuSeparator(viewMenu); win->viewAdjustWindowSize = createToggleMenuItem(viewMenu, "viewAdjustWindowSize", "Adjust Window Size", 'W', TRUE, NULL, NULL, ViewAdjustWindowSizeCB, win); // preferences menu win->prefSingleInstanceButton = createToggleMenuItem(prefMenu, "prefSingleInstance", "Single Instance", 'S', FALSE, NULL, NULL, PrefSingleInstanceCB, win); } void go_fullscreen(Display *dsp, Window win) { XEvent xev; Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False); Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False); memset(&xev, 0, sizeof(xev)); xev.type = ClientMessage; xev.xclient.window = win; xev.xclient.message_type = wm_state; xev.xclient.format = 32; xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD xev.xclient.data.l[1] = fullscreen; xev.xclient.data.l[2] = 0; XSendEvent(dsp, DefaultRootWindow(dsp), False, SubstructureNotifyMask, &xev); } static Atom net_wm_state; static Atom net_wm_state_fullscreen; static int net_wm_atoms_initialized = 0; void WindowFullscreen(MainWindow *win, bool enableFullscreen) { Display *dpy = XtDisplay(win->window); // init net_wm_state atoms if(!net_wm_atoms_initialized) { net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); net_wm_atoms_initialized = 1; } WindowMenubarSetVisible(win, !enableFullscreen); if(enableFullscreen && !win->fullscreen) { XtUnmanageChild(main_window->menubar); main_window->fullscreen = TRUE; } else if(!enableFullscreen && win->fullscreen) { XtManageChild(main_window->menubar); main_window->fullscreen = FALSE; } WindowShowSidebar(win, enableFullscreen ? false : win->sidebarvisible); XEvent ev; memset(&ev, 0, sizeof(XEvent)); ev.type = ClientMessage; ev.xclient.window = XtWindow(win->window); ev.xclient.message_type = net_wm_state; ev.xclient.format = 32; ev.xclient.data.l[0] = enableFullscreen ? 1 : 0; ev.xclient.data.l[1] = net_wm_state_fullscreen; ev.xclient.data.l[2] = 0; XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask | SubstructureRedirectMask, &ev); } void WindowMenubarSetVisible(MainWindow *win, bool visible) { if(visible) { XtManageChild(main_window->menubar); XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, main_window->menubar, NULL); } else { XtUnmanageChild(main_window->menubar); XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_FORM, NULL); } } static void filedialog_end( Widget widget, MainWindow *data, XmFileSelectionBoxCallbackStruct *selection) { XtUnmanageChild(widget); XtDestroyWidget(widget); } static void filedialog_select( Widget widget, MainWindow *data, XmFileSelectionBoxCallbackStruct *selection) { char *value = NULL; if(selection->value) { XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value); if(value) { PlayListAddFile(data, value); PlayListPlayNext(data, true); XtFree(value); } } filedialog_end(widget, data, NULL); } static void FileOpenCB(Widget w, void *udata, void *cdata) { MainWindow *win = main_window; Arg args[16]; int n = 0; XtSetArg(args[n], XnNshowViewMenu, 1); n++; Widget dialog = XnCreateFileSelectionDialog(win->window, "dialog", args, n); XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, win); XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_end, win); Widget dirUp = XnFileSelectionBoxGetChild(dialog, XnFSB_DIR_UP_BUTTON); XtUnmanageChild(dirUp); XtManageChild(dialog); } static void FileQuitCB(Widget w, void *udata, void *cdata) { WindowClosePlayer(main_window); ApplicationExit(); } static void PlayRepeatCB(Widget w, void *udata, void *cdata) { MainWindow *win = udata; win->playlist.repeatTrack = XmToggleButtonGadgetGetState(w); win->playlist.repeatList = 0; win->playlist.autoplayFolder = 0; win->playlist.random = 0; XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL); XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL); XtVaSetValues(win->playRandom, XmNset, 0, NULL); } static void PlayRepeatListCB(Widget w, void *udata, void *cdata) { MainWindow *win = udata; win->playlist.repeatList = XmToggleButtonGadgetGetState(w); win->playlist.repeatTrack = 0; win->playlist.autoplayFolder = 0; win->playlist.random = 0; XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL); XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL); XtVaSetValues(win->playRandom, XmNset, 0, NULL); } static void PlayAutoPlayCB(Widget w, void *udata, void *cdata) { MainWindow *win = udata; win->playlist.autoplayFolder = XmToggleButtonGadgetGetState(w); win->playlist.repeatTrack = 0; win->playlist.repeatList = 0; win->playlist.random = 0; XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL); XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL); XtVaSetValues(win->playRandom, XmNset, 0, NULL); } static void PlayRandomCB(Widget w, void *udata, void *cdata) { MainWindow *win = udata; win->playlist.random = XmToggleButtonGadgetGetState(w); win->playlist.repeatTrack = 0; win->playlist.repeatList = 0; win->playlist.autoplayFolder = 0; XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL); XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL); XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL); } static void ViewFullscreenCB(Widget w, void *udata, void *cdata) { if(main_window->fullscreen) { WindowFullscreen(main_window, FALSE); } else { WindowFullscreen(main_window, TRUE); } } static void ViewSidebarCB(Widget w, void *udata, void *cdata) { MainWindow *win = udata; XmToggleButtonCallbackStruct *cb = cdata; win->sidebarvisible = cb->set; WindowShowSidebar(win, cb->set); } static void ViewAdjustWindowSizeCB(Widget w, void *udata, void *cdata) { MainWindow *win = udata; win->adjustWindowSize = XmToggleButtonGadgetGetState(w); } static void PrefSingleInstanceCB(Widget w, void *udata, void *cdata) { MainWindow *win = udata; win->singleInstance = XmToggleButtonGadgetGetState(w); Display *dp = XtDisplay(w); if(!win->singleInstance) { ShutdownInstanceSocket(dp); return; } Bool disable_item = False; if(CreateSingleInstanceSocket(dp, &disable_item)) { // TODO: err disable_item = True; } if(disable_item) { win->singleInstance = 0; XmToggleButtonGadgetSetState(w, False, False); } } void WindowAdjustAspectRatio(MainWindow *win) { if(!win->player) return; if(!win->player->isactive || win->player->width <= 0 || win->player->height <= 0) return; // we have a running player width video // adjust window aspect ratio (the window aspect ratio is different from // the video, because of window decoration, menubar and other extra controls) Dimension win_width, win_height; XtVaGetValues(win->window, XmNwidth, &win_width, XmNheight, &win_height, NULL); Dimension player_width, player_height; XtVaGetValues(win->player_widget, XmNwidth, &player_width, XmNheight, &player_height, NULL); double r = (double)win->player->width / (double)win->player->height; double p_width = player_width; double p_height = p_width / r; Dimension new_width = p_width + win_width - player_width; Dimension new_height = p_height + win_height - player_height; XSizeHints hints; hints.flags = PAspect; hints.min_aspect.x = new_width; hints.min_aspect.y = new_height; hints.max_aspect.x = new_width; hints.max_aspect.y = new_height; XSetWMNormalHints(XtDisplay(win->window), XtWindow(win->window), &hints); } void WindowClosePlayer(MainWindow *win) { if(win->player) { PlayerDestroy(win->player); } win->player = NULL; WindowShowPlayerCursor(win); } void WindowHidePlayerCursor(MainWindow *win) { if(!win->cursorhidden && win->player && win->player->window != 0) { XDefineCursor(XtDisplay(win->player_widget), XtWindow(win->player_widget), blank_cursor); win->cursorhidden = True; XFlush(XtDisplay(win->player_widget)); } } void WindowShowPlayerCursor(MainWindow *win) { if(win->cursorhidden && win->player && win->player->window != 0) { XDefineCursor(XtDisplay(win->player_widget), XtWindow(win->player_widget), None); XFlush(XtDisplay(win->player_widget)); } win->cursorhidden = False; } void WindowShowSidebar(MainWindow *win, bool visible) { if(visible) { XtManageChild(win->sidebar_scrolledwindow); XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, win->sidebar_scrolledwindow, NULL); } else { XtUnmanageChild(win->sidebar_scrolledwindow); XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_FORM, NULL); } } void WindowUpdate(MainWindow *win) { SidebarRepaint(win->sidebar); }