2 * Copyright 2022 Olaf Wintermann
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
37 static MainWindow *main_window;
39 static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *args, int nargs);
41 static void FileOpenCB(Widget w, void *udata, void *cdata);
42 static void FileQuitCB(Widget w, void *udata, void *cdata);
43 static void PlayRepeatCB(Widget w, void *udata, void *cdata);
44 static void PlayRepeatListCB(Widget w, void *udata, void *cdata);
45 static void PlayAutoPlayCB(Widget w, void *udata, void *cdata);
46 static void PlayRandomCB(Widget w, void *udata, void *cdata);
47 static void ViewFullscreenCB(Widget w, void *udata, void *cdata);
48 static void ViewSidebarCB(Widget w, void *udata, void *cdata);
49 static void ViewAdjustWindowSizeCB(Widget w, void *udata, void *cdata);
50 static void PrefSingleInstanceCB(Widget w, void *udata, void *cdata);
52 static void WindowRealized(MainWindow *win);
54 static int blank_cursor_init = 0;
55 static Pixmap blank_cursor_pixmap;
56 static Cursor blank_cursor;
58 static void init_blank_cursor(Widget w) {
63 blank_cursor_pixmap = XCreateBitmapFromData(XtDisplay(w), XtWindow(w), &data, 1, 1);
64 if(!blank_cursor_pixmap) return;
66 blank_cursor = XCreatePixmapCursor(XtDisplay(w), blank_cursor_pixmap, blank_cursor_pixmap, &c, &c, 0, 0);
68 XFreePixmap(XtDisplay(w), blank_cursor_pixmap);
69 blank_cursor_init = 1;
72 static void window_close_handler(Widget window, void *udata, void *cdata) {
73 FileQuitCB(window, NULL, NULL);
76 static unsigned int keycodeF;
78 static void windowKeyEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
79 MainWindow *win = data;
80 if(win->fullscreen && event->xkey.keycode == keycodeF) {
81 WindowFullscreen(main_window, FALSE);
86 static int main_window_is_realized = 0;
88 static void resizeEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
89 if(!main_window_is_realized) {
90 if(XtIsRealized(widget)) {
91 main_window_is_realized = 1;
95 WindowAdjustAspectRatio(data);
98 static void WindowRealized(MainWindow *win) {
99 char *open_file = GetOpenFileArg();
101 PlayListAddFile(win, open_file);
102 PlayListPlayNext(win, true);
106 if(!blank_cursor_init) {
107 init_blank_cursor(win->player_widget);
110 XdndEnable(win->window);
113 static void playerWidgetInputCB(Widget widget, XtPointer u, XtPointer c) {
115 XmDrawingAreaCallbackStruct *cb = c;
117 if(win->player && win->player->isactive) {
118 PlayerHandleInput(win, win->player, cb);
122 static void windowGrabButton(MainWindow *win) {
129 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | EnterWindowMask | LeaveWindowMask,
134 win->buttongrab = True;
137 static void playerEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
138 MainWindow *win = data;
139 int etype = event->type;
142 if(etype == EnterNotify) {
144 windowGrabButton(win);
147 if(etype == LeaveNotify) {
149 //XtUngrabButton(win->player_widget, AnyButton, AnyModifier);
150 //win->buttongrab = False;
155 if(etype == ButtonPress || etype == ButtonRelease || etype == KeyPress || etype == KeyRelease) {
156 //printf("button press\n");
160 if(!win->player || win->player->window == 0) return;
162 WindowHandlePlayerEvent(win, event);
165 // redirect key events to the player window
166 //printf("redirect\n");
167 event->xkey.window = win->player->window;
169 XtDisplay(win->player_widget),
177 #define IGNORE_MOTION_THRESHOLD_MS 1000
178 #define MOTION_POS_THRESHOLD_PIX 5
179 #define OSD_BOTTOM_THRESHOLD 0.09
181 #define DOUBLE_CLICK_TIME_MS 500
183 void WindowHandlePlayerEvent(MainWindow *win, XEvent *event) {
184 // event handler for intercepted player mouse events
185 // win->player is not NULL
187 int etype = event->type;
189 if(etype == MotionNotify) {
190 Time cur_motion_time = event->xmotion.time;
192 win->motion_playback_time = win->player->playback_time;
195 int x = event->xmotion.x_root;
196 int y = event->xmotion.y_root;
197 if(win->cursorhidden && cur_motion_time - win->player_event_time < IGNORE_MOTION_THRESHOLD_MS) {
198 int diff_x = abs(x - win->mouse_x);
199 int diff_y = abs(y - win->mouse_y);
200 if(diff_x > MOTION_POS_THRESHOLD_PIX || diff_y > MOTION_POS_THRESHOLD_PIX) {
201 WindowShowPlayerCursor(win);
207 win->player_event_time = cur_motion_time;
208 win->motion_playback_time = win->player->playback_time;
212 if(win->pwbuttonpressed) {
213 Display *dp = XtDisplay(win->window);
215 XtUngrabPointer(win->player_widget, CurrentTime);
218 memset(&xev, 0, sizeof(xev));
219 xev.type = ClientMessage;
220 xev.xclient.message_type = XInternAtom(dp, "_NET_WM_MOVERESIZE", False);
221 xev.xclient.window = XtWindow(win->window);
222 xev.xclient.format = 32;
223 xev.xclient.data.l[0] = x;
224 xev.xclient.data.l[1] = y;
225 xev.xclient.data.l[2] = 8; // _NET_WM_MOVERESIZE_MOVE
226 xev.xclient.data.l[3] = 1; // button1
227 xev.xclient.data.l[4] = 1; // source indication
229 XSendEvent(dp, DefaultRootWindow(dp), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
231 win->pwbuttonpressed = FALSE;
233 } else if(etype == ButtonPress) {
234 Time t = event->xbutton.time;
236 int yi = win->player_widget->core.height - event->xbutton.y;
237 if((float)yi/(float)win->player_widget->core.height < OSD_BOTTOM_THRESHOLD) {
238 win->button_press_time = 0;
240 if(t - win->button_press_time < DOUBLE_CLICK_TIME_MS) {
242 WindowFullscreen(main_window, !win->fullscreen);
243 win->button_press_time = 0;
245 win->button_press_time = t;
247 win->pwbuttonpressed = 1;
249 } else if(etype == ButtonRelease) {
250 win->player_event_time = event->xbutton.time;
251 win->pwbuttonpressed = FALSE;
257 MainWindow* WindowCreate(Display *display) {
261 MainWindow *window = malloc(sizeof(MainWindow));
262 memset(window, 0, sizeof(MainWindow));
263 main_window = window;
267 XtSetArg(args[n], XmNtitle, APP_NAME); n++;
268 window->window = XtAppCreateShell(
271 applicationShellWidgetClass,
272 //vendorShellWidgetClass,
278 Atom wm_delete_window;
279 wm_delete_window = XmInternAtom(
283 XmAddWMProtocolCallback(
286 window_close_handler,
290 XtAddEventHandler(window->window, StructureNotifyMask, False, resizeEH, window);
293 XtSetArg(args[n], XmNwidth, 360); n++;
294 XtSetArg(args[n], XmNheight, 220); n++;
295 XtSetArg(args[n], XmNshadowThickness, 0); n++;
296 Widget container = XmCreateForm(window->window, "form", args, n);
297 XtManageChild(container);
298 XtAddEventHandler(container, KeyPressMask, FALSE, windowKeyEH, window);
301 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
302 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
303 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
304 WindowCreateMenu(window, container, args, n);
307 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
308 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
309 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
310 XtSetArg(args[n], XmNtopWidget, window->menubar); n++;
311 XtSetArg(args[n], XmNwidth, 300); n++;
312 XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
313 XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmAS_NEEDED); n++;
314 XtSetArg(args[n], XmNspacing, 0); n++;
315 XtSetArg(args[n], XmNshadowThickness, 0); n++;
316 window->sidebar_scrolledwindow = XmCreateScrolledWindow(container, "sw_sidebar", args, n);
317 window->sidebar = CreateSidebar(window->sidebar_scrolledwindow, "sidebar", args, 0);
318 SidebarSetWindow(window->sidebar, window);
319 XtManageChild(window->sidebar);
320 //XtManageChild(window->sidebar);
323 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
324 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
325 XtSetArg(args[n], XmNrightWidget, window->sidebar); n++;
326 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
327 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
328 XtSetArg(args[n], XmNtopWidget, window->menubar); n++;
329 XtSetArg(args[n], XmNbackground, BlackPixelOfScreen(XtScreen(window->window))); n++;
330 window->player_widget = XmCreateDrawingArea(container, "player", args, n);
331 XtManageChild(window->player_widget);
332 XtAddCallback(window->player_widget, XmNinputCallback, playerWidgetInputCB, window);
333 XmProcessTraversal(window->player_widget, XmTRAVERSE_CURRENT);
334 XtAddEventHandler(window->player_widget, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
335 EnterWindowMask | KeyPressMask | KeyReleaseMask |
336 LeaveWindowMask, FALSE, playerEH, window);
340 keycodeF = XKeysymToKeycode(XtDisplay(window->window), XStringToKeysym("F"));
343 PlayListInit(window);
345 window->adjustWindowSize = true; // auto adjust window size by default
350 MainWindow* GetMainWindow(void) {
354 void WindowShow(MainWindow *win) {
355 XtRealizeWidget(win->window);
359 * Creates a XmPushButton menu item
361 static Widget createMenuItem(
366 const char *accelerator,
367 char *accelerator_text,
368 XtCallbackProc callback,
374 XmString s1 = XmStringCreateSimple(label);
375 XtSetArg(args[n], XmNlabelString, s1); n++;
376 XtSetArg(args[n], XmNmnemonic, mnemonic); n++;
378 if(accelerator && accelerator_text) {
379 at = XmStringCreateSimple(accelerator_text);
380 XtSetArg(args[n], XmNaccelerator, accelerator); n++;
381 XtSetArg(args[n], XmNacceleratorText, at); n++;
384 Widget menuItem = XmCreatePushButtonGadget(menu, name, args, n);
385 XtManageChild(menuItem);
387 if(at) XmStringFree(at);
390 XtAddCallback(menuItem, XmNactivateCallback, (XtCallbackProc)callback, cbData);
397 * Creates a XmToggleButton menu item
399 static Widget createToggleMenuItem(
404 Boolean defaultValue,
405 const char *accelerator,
406 char *accelerator_text,
407 XtCallbackProc callback,
413 XmString s1 = XmStringCreateSimple(label);
414 XtSetArg(args[n], XmNlabelString, s1); n++;
415 XtSetArg(args[n], XmNmnemonic, mnemonic); n++;
416 XtSetArg(args[n], XmNset, defaultValue); n++;
418 if(accelerator && accelerator_text) {
419 at = XmStringCreateSimple(accelerator_text);
420 XtSetArg(args[n], XmNaccelerator, accelerator); n++;
421 XtSetArg(args[n], XmNacceleratorText, at); n++;
424 Widget menuItem = XmCreateToggleButtonGadget(menu, name, args, n);
425 XtManageChild(menuItem);
427 if(at) XmStringFree(at);
430 XtAddCallback(menuItem, XmNvalueChangedCallback, (XtCallbackProc)callback, cbData);
437 * Creates a menu separator
439 static Widget createMenuSeparator(Widget menu) {
440 Widget w = XmCreateSeparator(menu, "separator", NULL, 0);
445 static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nmbargs) {
446 Widget menubar = XmCreateMenuBar(parent, "menubar", mbargs, nmbargs);
447 XtManageChild(menubar);
448 win->menubar = menubar;
454 XmString s = XmStringCreateSimple("File");
455 XtVaCreateManagedWidget(
457 xmCascadeButtonWidgetClass,
462 Widget fileMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 0, NULL, NULL);
464 s = XmStringCreateSimple("Playback");
465 XtVaCreateManagedWidget(
467 xmCascadeButtonWidgetClass,
472 Widget playMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 1, NULL, NULL);
474 s = XmStringCreateSimple("View");
475 Widget viewMenuItem = XtVaCreateManagedWidget(
477 xmCascadeButtonWidgetClass,
482 Widget viewMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 2, NULL, NULL);
484 s = XmStringCreateSimple("Preferences");
485 Widget prefMenuItem = XtVaCreateManagedWidget(
487 xmCascadeButtonWidgetClass,
492 Widget prefMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 3, NULL, NULL);
495 createMenuItem(fileMenu, "fileOpen", "Open...", 'O', "Ctrl<Key>O", "Ctrl+O", FileOpenCB, NULL);
496 createMenuItem(fileMenu, "fileQuit", "Exit", 'E', "Ctrl<Key>Q", "Ctrl+Q", FileQuitCB, NULL);
499 win->playRepeatTrackButton = createToggleMenuItem(playMenu, "playRepeatTrack", "Repeat", 'R', False, NULL, NULL, PlayRepeatCB, win);
500 win->playRepeatListButton = createToggleMenuItem(playMenu, "playRepeatList", "Repeat List", 'L', False, NULL, NULL, PlayRepeatListCB, win);
501 win->playRandom = createToggleMenuItem(playMenu, "playRandom", "Random Playback", 'P', False, NULL, NULL, PlayRandomCB, win);
502 win->playAutoPlayButton = createToggleMenuItem(playMenu, "playAutoNext", "Autoplay Folder", 'A', False, NULL, NULL, PlayAutoPlayCB, win);
503 XtVaSetValues(win->playRepeatTrackButton, XmNindicatorType, XmONE_OF_MANY, NULL);
504 XtVaSetValues(win->playRepeatListButton, XmNindicatorType, XmONE_OF_MANY, NULL);
505 XtVaSetValues(win->playAutoPlayButton, XmNindicatorType, XmONE_OF_MANY, NULL);
506 XtVaSetValues(win->playRandom, XmNindicatorType, XmONE_OF_MANY, NULL);
509 createMenuItem(viewMenu, "viewFullscreen", "Fullscreen", 'F', "<Key>F", "F", ViewFullscreenCB, NULL);
510 win->viewSidebarButton = createToggleMenuItem(viewMenu, "viewSidebar", "View Sidebar", 'S', False, NULL, NULL, ViewSidebarCB, win);
512 createMenuSeparator(viewMenu);
514 win->viewAdjustWindowSize = createToggleMenuItem(viewMenu, "viewAdjustWindowSize", "Adjust Window Size", 'W', TRUE, NULL, NULL, ViewAdjustWindowSizeCB, win);
517 win->prefSingleInstanceButton = createToggleMenuItem(prefMenu, "prefSingleInstance", "Single Instance", 'S', FALSE, NULL, NULL, PrefSingleInstanceCB, win);
520 void go_fullscreen(Display *dsp, Window win)
523 Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False);
524 Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False);
525 memset(&xev, 0, sizeof(xev));
526 xev.type = ClientMessage;
527 xev.xclient.window = win;
528 xev.xclient.message_type = wm_state;
529 xev.xclient.format = 32;
530 xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
531 xev.xclient.data.l[1] = fullscreen;
532 xev.xclient.data.l[2] = 0;
533 XSendEvent(dsp, DefaultRootWindow(dsp), False,
534 SubstructureNotifyMask, &xev);
537 static Atom net_wm_state;
538 static Atom net_wm_state_fullscreen;
539 static int net_wm_atoms_initialized = 0;
541 void WindowFullscreen(MainWindow *win, bool enableFullscreen) {
542 Display *dpy = XtDisplay(win->window);
544 // init net_wm_state atoms
545 if(!net_wm_atoms_initialized) {
546 net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
547 net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
548 net_wm_atoms_initialized = 1;
551 WindowMenubarSetVisible(win, !enableFullscreen);
552 if(enableFullscreen && !win->fullscreen) {
553 XtUnmanageChild(main_window->menubar);
554 main_window->fullscreen = TRUE;
555 } else if(!enableFullscreen && win->fullscreen) {
556 XtManageChild(main_window->menubar);
557 main_window->fullscreen = FALSE;
560 WindowShowSidebar(win, enableFullscreen ? false : win->sidebarvisible);
563 memset(&ev, 0, sizeof(XEvent));
564 ev.type = ClientMessage;
565 ev.xclient.window = XtWindow(win->window);
566 ev.xclient.message_type = net_wm_state;
567 ev.xclient.format = 32;
568 ev.xclient.data.l[0] = enableFullscreen ? 1 : 0;
569 ev.xclient.data.l[1] = net_wm_state_fullscreen;
570 ev.xclient.data.l[2] = 0;
571 XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask | SubstructureRedirectMask, &ev);
574 void WindowMenubarSetVisible(MainWindow *win, bool visible) {
576 XtManageChild(main_window->menubar);
577 XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, main_window->menubar, NULL);
579 XtUnmanageChild(main_window->menubar);
580 XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_FORM, NULL);
584 static void filedialog_end(
587 XmFileSelectionBoxCallbackStruct *selection)
589 XtUnmanageChild(widget);
590 XtDestroyWidget(widget);
593 static void filedialog_select(
596 XmFileSelectionBoxCallbackStruct *selection)
599 if(selection->value) {
600 XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value);
602 PlayListAddFile(data, value);
603 PlayListPlayNext(data, true);
607 filedialog_end(widget, data, NULL);
613 static void FileOpenCB(Widget w, void *udata, void *cdata) {
614 MainWindow *win = main_window;
619 XtSetArg(args[n], XnNshowViewMenu, 1); n++;
620 Widget dialog = XnCreateFileSelectionDialog(win->window, "dialog", args, n);
621 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, win);
622 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_end, win);
624 Widget dirUp = XnFileSelectionBoxGetChild(dialog, XnFSB_DIR_UP_BUTTON);
625 XtUnmanageChild(dirUp);
627 XtManageChild(dialog);
630 static void FileQuitCB(Widget w, void *udata, void *cdata) {
631 WindowClosePlayer(main_window);
635 static void PlayRepeatCB(Widget w, void *udata, void *cdata) {
636 MainWindow *win = udata;
637 win->playlist.repeatTrack = XmToggleButtonGadgetGetState(w);
638 win->playlist.repeatList = 0;
639 win->playlist.autoplayFolder = 0;
640 win->playlist.random = 0;
641 XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
642 XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
643 XtVaSetValues(win->playRandom, XmNset, 0, NULL);
646 static void PlayRepeatListCB(Widget w, void *udata, void *cdata) {
647 MainWindow *win = udata;
648 win->playlist.repeatList = XmToggleButtonGadgetGetState(w);
649 win->playlist.repeatTrack = 0;
650 win->playlist.autoplayFolder = 0;
651 win->playlist.random = 0;
652 XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
653 XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
654 XtVaSetValues(win->playRandom, XmNset, 0, NULL);
657 static void PlayAutoPlayCB(Widget w, void *udata, void *cdata) {
658 MainWindow *win = udata;
659 win->playlist.autoplayFolder = XmToggleButtonGadgetGetState(w);
660 win->playlist.repeatTrack = 0;
661 win->playlist.repeatList = 0;
662 win->playlist.random = 0;
663 XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
664 XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
665 XtVaSetValues(win->playRandom, XmNset, 0, NULL);
668 static void PlayRandomCB(Widget w, void *udata, void *cdata) {
669 MainWindow *win = udata;
670 win->playlist.random = XmToggleButtonGadgetGetState(w);
671 win->playlist.repeatTrack = 0;
672 win->playlist.repeatList = 0;
673 win->playlist.autoplayFolder = 0;
674 XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
675 XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
676 XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
679 static void ViewFullscreenCB(Widget w, void *udata, void *cdata) {
680 if(main_window->fullscreen) {
681 WindowFullscreen(main_window, FALSE);
683 WindowFullscreen(main_window, TRUE);
687 static void ViewSidebarCB(Widget w, void *udata, void *cdata) {
688 MainWindow *win = udata;
689 XmToggleButtonCallbackStruct *cb = cdata;
690 win->sidebarvisible = cb->set;
691 WindowShowSidebar(win, cb->set);
694 static void ViewAdjustWindowSizeCB(Widget w, void *udata, void *cdata) {
695 MainWindow *win = udata;
696 win->adjustWindowSize = XmToggleButtonGadgetGetState(w);
699 static void PrefSingleInstanceCB(Widget w, void *udata, void *cdata) {
700 MainWindow *win = udata;
701 win->singleInstance = XmToggleButtonGadgetGetState(w);
703 Display *dp = XtDisplay(w);
705 if(!win->singleInstance) {
706 ShutdownInstanceSocket(dp);
710 Bool disable_item = False;
711 if(CreateSingleInstanceSocket(dp, &disable_item)) {
716 win->singleInstance = 0;
717 XmToggleButtonGadgetSetState(w, False, False);
721 void WindowAdjustAspectRatio(MainWindow *win) {
722 if(!win->player) return;
723 if(!win->player->isactive || win->player->width <= 0 || win->player->height <= 0) return;
725 // we have a running player width video
726 // adjust window aspect ratio (the window aspect ratio is different from
727 // the video, because of window decoration, menubar and other extra controls)
729 Dimension win_width, win_height;
730 XtVaGetValues(win->window, XmNwidth, &win_width, XmNheight, &win_height, NULL);
731 Dimension player_width, player_height;
732 XtVaGetValues(win->player_widget, XmNwidth, &player_width, XmNheight, &player_height, NULL);
734 double r = (double)win->player->width / (double)win->player->height;
735 double p_width = player_width;
736 double p_height = p_width / r;
738 Dimension new_width = p_width + win_width - player_width;
739 Dimension new_height = p_height + win_height - player_height;
742 hints.flags = PAspect;
743 hints.min_aspect.x = new_width;
744 hints.min_aspect.y = new_height;
745 hints.max_aspect.x = new_width;
746 hints.max_aspect.y = new_height;
747 XSetWMNormalHints(XtDisplay(win->window), XtWindow(win->window), &hints);
750 void WindowClosePlayer(MainWindow *win) {
752 PlayerDestroy(win->player);
755 WindowShowPlayerCursor(win);
758 void WindowHidePlayerCursor(MainWindow *win) {
759 if(!win->cursorhidden && win->player && win->player->window != 0) {
760 XDefineCursor(XtDisplay(win->player_widget), XtWindow(win->player_widget), blank_cursor);
761 win->cursorhidden = True;
762 XFlush(XtDisplay(win->player_widget));
766 void WindowShowPlayerCursor(MainWindow *win) {
767 if(win->cursorhidden && win->player && win->player->window != 0) {
768 XDefineCursor(XtDisplay(win->player_widget), XtWindow(win->player_widget), None);
769 XFlush(XtDisplay(win->player_widget));
771 win->cursorhidden = False;
774 void WindowShowSidebar(MainWindow *win, bool visible) {
776 XtManageChild(win->sidebar_scrolledwindow);
777 XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, win->sidebar_scrolledwindow, NULL);
779 XtUnmanageChild(win->sidebar_scrolledwindow);
780 XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_FORM, NULL);
784 void WindowUpdate(MainWindow *win) {
785 SidebarRepaint(win->sidebar);