3be3985a19d7208d11979df2eb45913aa7deb531
[uwplayer.git] / application / window.c
1 /*
2  * Copyright 2022 Olaf Wintermann
3  *
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:
10  * 
11  * The above copyright notice and this permission notice shall be included in 
12  * all copies or substantial portions of the Software.
13  *
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.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "window.h"
28 #include "main.h"
29 #include "player.h"
30 #include "playlist.h"
31 #include "xdnd.h"
32
33 #include "Fsb.h"
34 #include "Sidebar.h"
35
36 static MainWindow *main_window;
37
38 static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *args, int nargs);
39
40 static void FileOpenCB(Widget w, void *udata, void *cdata);
41 static void FileQuitCB(Widget w, void *udata, void *cdata);
42 static void PlayRepeatCB(Widget w, void *udata, void *cdata);
43 static void PlayRepeatListCB(Widget w, void *udata, void *cdata);
44 static void PlayAutoPlayCB(Widget w, void *udata, void *cdata);
45 static void ViewFullscreenCB(Widget w, void *udata, void *cdata);
46 static void ViewSidebarCB(Widget w, void *udata, void *cdata);
47
48 static void WindowRealized(MainWindow *win);
49
50 static int blank_cursor_init = 0;
51 static Pixmap blank_cursor_pixmap;
52 static Cursor blank_cursor;
53
54 static void init_blank_cursor(Widget w) {
55     char data = 0;
56     
57     XColor c;
58     
59     blank_cursor_pixmap = XCreateBitmapFromData(XtDisplay(w), XtWindow(w), &data, 1, 1);
60     if(!blank_cursor_pixmap) return;
61     
62     blank_cursor = XCreatePixmapCursor(XtDisplay(w), blank_cursor_pixmap, blank_cursor_pixmap, &c, &c, 0, 0);
63     
64     XFreePixmap(XtDisplay(w), blank_cursor_pixmap);
65     blank_cursor_init = 1;
66 }
67
68 static void window_close_handler(Widget window, void *udata, void *cdata) {
69     FileQuitCB(window, NULL, NULL);
70 }
71
72 static unsigned int keycodeF;
73
74 static void windowKeyEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
75     MainWindow *win = data;
76     if(win->fullscreen && event->xkey.keycode == keycodeF) {
77         WindowFullscreen(main_window, FALSE);
78         *dispatch = FALSE;
79     }
80 }
81
82 static int main_window_is_realized = 0;
83
84 static void resizeEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {  
85     if(!main_window_is_realized) {
86         if(XtIsRealized(widget)) {
87             main_window_is_realized = 1;
88             WindowRealized(data);
89         }
90     }
91     WindowAdjustAspectRatio(data);
92 }
93
94 static void WindowRealized(MainWindow *win) {
95     char *open_file = GetOpenFileArg();
96     if(open_file) {
97         PlayListAddFile(win, open_file);
98         PlayListPlayNext(win, true);
99         CleanOpenFileArg();
100     }
101     
102     if(!blank_cursor_init) {
103         init_blank_cursor(win->player_widget);
104     }
105     
106     XdndEnable(win->window);
107 }
108
109 static void playerWidgetInputCB(Widget widget, XtPointer u, XtPointer c) {
110     MainWindow *win = u;
111     XmDrawingAreaCallbackStruct *cb = c;
112     
113     if(win->player && win->player->isactive) {
114         PlayerHandleInput(win, win->player, cb);
115     }
116 }
117
118 static void windowGrabButton(MainWindow *win) {
119     //printf("grab\n");
120     XtGrabButton(
121                 win->player_widget,
122                 AnyButton,
123                 AnyModifier,
124                 True,
125                 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | EnterWindowMask | LeaveWindowMask,
126                 GrabModeAsync,
127                 GrabModeAsync,
128                 None,
129                 None);
130     win->buttongrab = True;
131 }
132
133 static void playerEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
134     MainWindow *win = data;
135     int etype = event->type;
136     
137     ///*
138     if(etype == EnterNotify) {
139         //printf("enter\n");
140         windowGrabButton(win);
141         return;
142     }
143     if(etype == LeaveNotify) {
144         //printf("leave\n");
145         //XtUngrabButton(win->player_widget, AnyButton, AnyModifier); 
146         //win->buttongrab = False;
147         return;
148     }
149     
150     int pass = 0;
151     if(etype == ButtonPress || etype == ButtonRelease || etype == KeyPress || etype == KeyRelease) {
152         //printf("button press\n");
153         pass = 1;
154     }
155     
156     if(!win->player || win->player->window == 0) return;
157     
158     WindowHandlePlayerEvent(win, event);
159     
160     if(pass) {
161         // redirect key events to the player window
162         //printf("redirect\n");
163         event->xkey.window = win->player->window;
164         XSendEvent(
165                 XtDisplay(win->player_widget),
166                 win->player->window,
167                 True,
168                 0,
169                 event);
170     }
171 }
172
173 #define IGNORE_MOTION_THRESHOLD_MS 1000
174 #define MOTION_POS_THRESHOLD_PIX   5
175 #define OSD_BOTTOM_THRESHOLD       0.09
176
177 #define DOUBLE_CLICK_TIME_MS       500
178
179 void WindowHandlePlayerEvent(MainWindow *win, XEvent *event) {
180     // event handler for intercepted player mouse events
181     // win->player is not NULL
182     
183     int etype = event->type;
184     
185     if(etype == MotionNotify) {
186         Time cur_motion_time = event->xmotion.time;
187         if(win->player) {
188             win->motion_playback_time = win->player->playback_time;
189         }
190         
191         int x = event->xmotion.x_root;
192         int y = event->xmotion.y_root;
193         if(win->cursorhidden && cur_motion_time - win->player_event_time < IGNORE_MOTION_THRESHOLD_MS) {
194             int diff_x = abs(x - win->mouse_x);
195             int diff_y = abs(y - win->mouse_y);
196             if(diff_x > MOTION_POS_THRESHOLD_PIX || diff_y > MOTION_POS_THRESHOLD_PIX) {
197                 WindowShowPlayerCursor(win);
198             }
199         } else {
200             win->mouse_x = x;
201             win->mouse_y = y;
202         }
203         win->player_event_time = cur_motion_time;
204         win->motion_playback_time = win->player->playback_time;
205         
206         
207         
208         if(win->pwbuttonpressed) {
209             Display *dp = XtDisplay(win->window);
210                 
211             XtUngrabPointer(win->player_widget, CurrentTime);
212
213             XEvent xev;
214             memset(&xev, 0, sizeof(xev));
215             xev.type = ClientMessage;
216             xev.xclient.message_type = XInternAtom(dp, "_NET_WM_MOVERESIZE", False);
217             xev.xclient.window = XtWindow(win->window);
218             xev.xclient.format = 32;
219             xev.xclient.data.l[0] = x;
220             xev.xclient.data.l[1] = y;
221             xev.xclient.data.l[2] = 8; // _NET_WM_MOVERESIZE_MOVE
222             xev.xclient.data.l[3] = 1; // button1
223             xev.xclient.data.l[4] = 1; // source indication
224
225             XSendEvent(dp, DefaultRootWindow(dp), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
226
227             win->pwbuttonpressed = FALSE;  
228         }
229     } else if(etype == ButtonPress) {
230         Time t = event->xbutton.time;
231         
232         int yi = win->player_widget->core.height - event->xbutton.y;
233         if((float)yi/(float)win->player_widget->core.height < OSD_BOTTOM_THRESHOLD) {
234             win->button_press_time = 0;
235         } else {
236             if(t - win->button_press_time < DOUBLE_CLICK_TIME_MS) {
237                 // double click
238                 WindowFullscreen(main_window, !win->fullscreen);
239                 win->button_press_time = 0;
240             } else {
241                 win->button_press_time = t;
242             }
243             win->pwbuttonpressed = 1;
244         }
245     } else if(etype == ButtonRelease) {
246         win->player_event_time = event->xbutton.time;
247         win->pwbuttonpressed = FALSE;
248     }
249 }
250
251
252
253 MainWindow* WindowCreate(Display *display) {
254     Arg args[32];
255     int n;
256      
257     MainWindow *window = malloc(sizeof(MainWindow));
258     memset(window, 0, sizeof(MainWindow));
259     main_window = window;
260       
261     // toplevel window
262     n = 0;
263     XtSetArg(args[n], XmNtitle, APP_NAME); n++;
264     window->window = XtAppCreateShell(
265             APP_NAME,
266             APP_CLASS,
267             applicationShellWidgetClass,
268             //vendorShellWidgetClass,
269             display,
270             args,
271             n);
272     
273     // close handler
274     Atom wm_delete_window;
275     wm_delete_window = XmInternAtom(
276             display,
277             "WM_DELETE_WINDOW",
278             0);
279     XmAddWMProtocolCallback(
280             window->window,
281             wm_delete_window,
282             window_close_handler,
283             window);
284     
285     // resize handler
286     XtAddEventHandler(window->window, StructureNotifyMask, False, resizeEH, window);
287     
288     n = 0;
289     XtSetArg(args[n], XmNwidth, 360); n++;
290     XtSetArg(args[n], XmNheight, 220); n++;
291     XtSetArg(args[n], XmNshadowThickness, 0); n++;
292     Widget container = XmCreateForm(window->window, "form", args, n);
293     XtManageChild(container);
294     XtAddEventHandler(container, KeyPressMask, FALSE, windowKeyEH, window);
295     
296     n = 0;
297     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
298     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
299     XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
300     WindowCreateMenu(window, container, args, n);
301     
302     n = 0;
303     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
304     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
305     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
306     XtSetArg(args[n], XmNtopWidget, window->menubar); n++;
307     XtSetArg(args[n], XmNwidth, 300); n++;
308     window->sidebar = CreateSidebar(container, "sidebar", args, n);
309     //XtManageChild(window->sidebar);
310        
311     n = 0;
312     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
313     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
314     XtSetArg(args[n], XmNrightWidget, window->sidebar); n++;
315     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
316     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
317     XtSetArg(args[n], XmNtopWidget, window->menubar); n++;
318     XtSetArg(args[n], XmNbackground, BlackPixelOfScreen(XtScreen(window->window))); n++;
319     window->player_widget = XmCreateDrawingArea(container, "player", args, n);
320     XtManageChild(window->player_widget);
321     XtAddCallback(window->player_widget, XmNinputCallback, playerWidgetInputCB, window);
322     XmProcessTraversal(window->player_widget, XmTRAVERSE_CURRENT);
323     XtAddEventHandler(window->player_widget, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
324                  EnterWindowMask | KeyPressMask | KeyReleaseMask |
325                   LeaveWindowMask, FALSE, playerEH, window);
326     
327      
328     // get F keycode
329     keycodeF = XKeysymToKeycode(XtDisplay(window->window), XStringToKeysym("F"));
330     
331     
332     PlayListInit(window);
333     
334     return window;
335 }
336
337 MainWindow* GetMainWindow(void) {
338     return main_window;
339 }
340
341 void WindowShow(MainWindow *win) {
342     XtRealizeWidget(win->window);
343 }
344
345 /*
346  * Creates a XmPushButton menu item
347  */
348 static Widget createMenuItem(
349         Widget menu,
350         char *name,
351         char *label,
352         char mnemonic,
353         const char *accelerator,
354         char *accelerator_text,
355         XtCallbackProc callback,
356         void *cbData)
357 {
358     Arg args[16];
359     int n = 0;
360     
361     XmString s1 = XmStringCreateSimple(label);
362     XtSetArg(args[n], XmNlabelString, s1); n++;
363     XtSetArg(args[n], XmNmnemonic, mnemonic); n++;
364     XmString at = NULL;
365     if(accelerator && accelerator_text) {
366         at = XmStringCreateSimple(accelerator_text);
367         XtSetArg(args[n], XmNaccelerator, accelerator); n++;
368         XtSetArg(args[n], XmNacceleratorText, at); n++;
369     }
370     
371     Widget menuItem = XmCreatePushButtonGadget(menu, name, args, n);
372     XtManageChild(menuItem);
373     XmStringFree(s1);
374     if(at) XmStringFree(at);
375     
376     if(callback) {
377         XtAddCallback(menuItem, XmNactivateCallback, (XtCallbackProc)callback, cbData);
378     }
379     
380     return menuItem;
381 }
382
383 /*
384  * Creates a XmToggleButton menu item
385  */
386 static Widget createToggleMenuItem(
387         Widget menu,
388         char *name,
389         char *label,
390         char mnemonic,
391         Boolean defaultValue,
392         const char *accelerator,
393         char *accelerator_text,
394         XtCallbackProc callback,
395         void *cbData)
396 {
397     Arg args[16];
398     int n = 0;
399     
400     XmString s1 = XmStringCreateSimple(label);
401     XtSetArg(args[n], XmNlabelString, s1); n++;
402     XtSetArg(args[n], XmNmnemonic, mnemonic); n++;
403     XtSetArg(args[n], XmNset, defaultValue); n++;
404     XmString at = NULL;
405     if(accelerator && accelerator_text) {
406         at = XmStringCreateSimple(accelerator_text);
407         XtSetArg(args[n], XmNaccelerator, accelerator); n++;
408         XtSetArg(args[n], XmNacceleratorText, at); n++;
409     }
410     
411     Widget menuItem = XmCreateToggleButtonGadget(menu, name, args, n);
412     XtManageChild(menuItem);
413     XmStringFree(s1);
414     if(at) XmStringFree(at);
415     
416     if(callback) {
417         XtAddCallback(menuItem, XmNvalueChangedCallback, (XtCallbackProc)callback, cbData);
418     }
419     
420     return menuItem;
421 }
422
423 static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nmbargs) {
424     Widget menubar = XmCreateMenuBar(parent, "menubar", mbargs, nmbargs);
425     XtManageChild(menubar);
426     win->menubar = menubar;
427     
428     Arg args[16];
429     int n;
430     
431     // menus
432     XmString s = XmStringCreateSimple("File");
433     XtVaCreateManagedWidget(
434             "menuitem",
435             xmCascadeButtonWidgetClass,
436             menubar,
437             XmNlabelString, s,
438             NULL);
439     XmStringFree(s);
440     Widget fileMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 0, NULL, NULL);
441     
442     s = XmStringCreateSimple("Playback");
443     XtVaCreateManagedWidget(
444             "menuitem",
445             xmCascadeButtonWidgetClass,
446             menubar,
447             XmNlabelString, s,
448             NULL);
449     XmStringFree(s);
450     Widget playMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 1, NULL, NULL);
451     
452     s = XmStringCreateSimple("View");
453     Widget viewMenuItem = XtVaCreateManagedWidget(
454             "menuitem",
455             xmCascadeButtonWidgetClass,
456             menubar,
457             XmNlabelString, s,
458             NULL);
459     XmStringFree(s);
460     Widget viewMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 2, NULL, NULL);
461     
462     // file menu
463     createMenuItem(fileMenu, "fileOpen", "Open...", 'O', "Ctrl<Key>O", "Ctrl+O", FileOpenCB, NULL);
464     createMenuItem(fileMenu, "fileQuit", "Exit", 'E', "Ctrl<Key>Q", "Ctrl+Q", FileQuitCB, NULL);
465     
466     // play menu
467     win->playRepeatTrackButton = createToggleMenuItem(playMenu, "playRepeatTrack", "Repeat", 'R', False, NULL, NULL, PlayRepeatCB, win);
468     win->playRepeatListButton = createToggleMenuItem(playMenu, "playRepeatList", "Repeat List", 'L', False, NULL, NULL, PlayRepeatListCB, win);
469     win->playAutoPlayButton = createToggleMenuItem(playMenu, "playAutoNext", "Autoplay Folder", 'A', False, NULL, NULL, PlayAutoPlayCB, win);
470     XtVaSetValues(win->playRepeatTrackButton, XmNindicatorType, XmONE_OF_MANY, NULL);
471     XtVaSetValues(win->playRepeatListButton, XmNindicatorType, XmONE_OF_MANY, NULL);
472     XtVaSetValues(win->playAutoPlayButton, XmNindicatorType, XmONE_OF_MANY, NULL);
473     
474     // view menu
475     createMenuItem(viewMenu, "viewFullscreen", "Fullscreen", 'F', "<Key>F", "F", ViewFullscreenCB, NULL);
476     win->viewSidebarButton = createToggleMenuItem(viewMenu, "viewSidebar", "View Sidebar", 'S', False, NULL, NULL, ViewSidebarCB, win);
477 }
478
479 void go_fullscreen(Display *dsp, Window win)
480 {
481   XEvent xev;
482   Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False);
483   Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False);
484   memset(&xev, 0, sizeof(xev));
485   xev.type = ClientMessage;
486   xev.xclient.window = win;
487   xev.xclient.message_type = wm_state;
488   xev.xclient.format = 32;
489   xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
490   xev.xclient.data.l[1] = fullscreen;
491   xev.xclient.data.l[2] = 0;
492   XSendEvent(dsp, DefaultRootWindow(dsp), False,
493     SubstructureNotifyMask, &xev);
494 }
495
496 static Atom net_wm_state;
497 static Atom net_wm_state_fullscreen;
498 static int net_wm_atoms_initialized = 0;
499
500 void WindowFullscreen(MainWindow *win, bool enableFullscreen) {
501     Display *dpy = XtDisplay(win->window);
502     
503     // init net_wm_state atoms
504     if(!net_wm_atoms_initialized) {
505         net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
506         net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
507         net_wm_atoms_initialized = 1;
508     }
509     
510     WindowMenubarSetVisible(win, !enableFullscreen);
511     if(enableFullscreen && !win->fullscreen) {
512         XtUnmanageChild(main_window->menubar);
513         main_window->fullscreen = TRUE;
514     } else if(!enableFullscreen && win->fullscreen) {
515         XtManageChild(main_window->menubar);
516         main_window->fullscreen = FALSE;
517     }
518     
519     XEvent ev;
520     memset(&ev, 0, sizeof(XEvent));
521     ev.type = ClientMessage;
522     ev.xclient.window = XtWindow(win->window);
523     ev.xclient.message_type = net_wm_state;
524     ev.xclient.format = 32;
525     ev.xclient.data.l[0] = enableFullscreen ? 1 : 0;
526     ev.xclient.data.l[1] = net_wm_state_fullscreen;
527     ev.xclient.data.l[2] = 0;
528     XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask | SubstructureRedirectMask, &ev);
529 }
530
531 void WindowMenubarSetVisible(MainWindow *win, bool visible) {
532     if(visible) {
533         XtManageChild(main_window->menubar);
534         XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, main_window->menubar, NULL);
535     } else {
536         XtUnmanageChild(main_window->menubar);
537         XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_FORM, NULL);
538     }
539 }
540
541 static void filedialog_end(
542         Widget widget,
543         MainWindow *data,
544         XmFileSelectionBoxCallbackStruct *selection)
545 {
546     XtUnmanageChild(widget);
547     XtDestroyWidget(widget);
548 }
549
550 static void filedialog_select(
551         Widget widget,
552         MainWindow *data,
553         XmFileSelectionBoxCallbackStruct *selection)
554 {
555     char *value = NULL;
556     if(selection->value) {
557         XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value);
558         if(value) {
559             PlayListAddFile(data, value);
560             PlayListPlayNext(data, true);
561             XtFree(value);
562         }
563     }
564     filedialog_end(widget, data, NULL);
565 }
566
567
568
569
570 static void FileOpenCB(Widget w, void *udata, void *cdata) {
571     MainWindow *win = main_window;
572     
573     Arg args[16];
574     int n = 0;
575     
576     XtSetArg(args[n], XnNshowViewMenu, 1); n++;
577     Widget dialog = XnCreateFileSelectionDialog(win->window, "dialog", args, n);
578     XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, win);
579     XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_end, win);
580     
581     Widget dirUp = XnFileSelectionBoxGetChild(dialog, XnFSB_DIR_UP_BUTTON);
582     XtUnmanageChild(dirUp);
583     
584     XtManageChild(dialog);
585 }
586
587 static void FileQuitCB(Widget w, void *udata, void *cdata) {
588     WindowClosePlayer(main_window);
589     ApplicationExit();
590 }
591
592 static void PlayRepeatCB(Widget w, void *udata, void *cdata) {
593     MainWindow *win = udata;
594     win->playlist.repeatTrack = XmToggleButtonGadgetGetState(w);
595     win->playlist.repeatList = 0;
596     win->playlist.autoplayFolder = 0;
597     XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
598     XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
599 }
600
601 static void PlayRepeatListCB(Widget w, void *udata, void *cdata) {
602     MainWindow *win = udata;
603     win->playlist.repeatList = XmToggleButtonGadgetGetState(w);
604     win->playlist.repeatTrack = 0;
605     win->playlist.autoplayFolder = 0;
606     XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
607     XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
608 }
609
610 static void PlayAutoPlayCB(Widget w, void *udata, void *cdata) {
611     MainWindow *win = udata;
612     win->playlist.autoplayFolder = XmToggleButtonGadgetGetState(w);
613     win->playlist.repeatTrack = 0;
614     win->playlist.repeatList = 0;
615     XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
616     XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
617 }
618
619 static void ViewFullscreenCB(Widget w, void *udata, void *cdata) {
620     if(main_window->fullscreen) {
621         WindowFullscreen(main_window, FALSE);
622     } else {
623         WindowFullscreen(main_window, TRUE);
624     }   
625 }
626
627 static void ViewSidebarCB(Widget w, void *udata, void *cdata) {
628     MainWindow *win = udata;
629     XmToggleButtonCallbackStruct *cb = cdata;
630     if(cb->set) {
631         WindowShowSidebar(win);
632     } else {
633         WindowHideSidebar(win);
634     }
635 }
636
637 void WindowAdjustAspectRatio(MainWindow *win) {
638     if(!win->player) return;
639     if(!win->player->isactive || win->player->width <= 0 || win->player->height <= 0) return;
640       
641     // we have a running player width video
642     // adjust window aspect ratio (the window aspect ratio is different from
643     // the video, because of window decoration, menubar and other extra controls)
644     
645     Dimension win_width, win_height;
646     XtVaGetValues(win->window, XmNwidth, &win_width, XmNheight, &win_height, NULL);
647     Dimension player_width, player_height;
648     XtVaGetValues(win->player_widget, XmNwidth, &player_width, XmNheight, &player_height, NULL);
649     
650     double r = (double)win->player->width / (double)win->player->height;
651     double p_width = player_width;
652     double p_height = p_width / r;
653     
654     Dimension new_width = p_width + win_width - player_width;
655     Dimension new_height = p_height + win_height - player_height;
656     
657     XSizeHints hints;
658     hints.flags = PAspect;
659     hints.min_aspect.x = new_width;
660     hints.min_aspect.y = new_height;
661     hints.max_aspect.x = new_width;
662     hints.max_aspect.y = new_height;
663     XSetWMNormalHints(XtDisplay(win->window), XtWindow(win->window), &hints);
664 }
665
666 void WindowClosePlayer(MainWindow *win) {
667     if(win->player) {
668         PlayerDestroy(win->player);
669     }
670     win->player = NULL;
671     WindowShowPlayerCursor(win);
672 }
673
674 void WindowHidePlayerCursor(MainWindow *win) {
675     if(!win->cursorhidden && win->player && win->player->window != 0) {
676         XDefineCursor(XtDisplay(win->player_widget), XtWindow(win->player_widget), blank_cursor);
677         win->cursorhidden = True;
678         XFlush(XtDisplay(win->player_widget));
679     }
680 }
681
682 void WindowShowPlayerCursor(MainWindow *win) {
683     if(win->cursorhidden && win->player && win->player->window != 0) {
684         XDefineCursor(XtDisplay(win->player_widget), XtWindow(win->player_widget), None);
685         XFlush(XtDisplay(win->player_widget));
686     }
687     win->cursorhidden = False;
688 }
689
690 void WindowHideSidebar(MainWindow *win) {
691     XtUnmanageChild(win->sidebar);
692     XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_FORM, NULL);
693 }
694
695 void WindowShowSidebar(MainWindow *win) {
696     XtManageChild(win->sidebar);
697     XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, win->sidebar, NULL);
698 }
699
700
701
702
703