9ec43234397b53539c1a0cb4b80909773d61d0a1
[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
31 #include "Fsb.h"
32
33 static MainWindow *main_window;
34
35 static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *args, int nargs);
36
37 static void FileOpenCB(Widget w, void *udata, void *cdata);
38 static void ViewFullscreenCB(Widget w, void *udata, void *cdata);
39
40 static void window_close_handler(Widget window, void *udata, void *cdata) {
41     ApplicationExit();
42 }
43
44 static unsigned int keycodeF;
45
46 static void windowKeyEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
47     MainWindow *win = data;
48     if(win->fullscreen && event->xkey.keycode == keycodeF) {
49         WindowFullscreen(main_window, FALSE);
50         *dispatch = FALSE;
51     }
52 }
53
54 MainWindow* WindowCreate(Display *display) {
55     Arg args[32];
56     int n;
57     
58     MainWindow *window = malloc(sizeof(MainWindow));
59     memset(window, 0, sizeof(MainWindow));
60     main_window = window;
61     
62     // toplevel window
63     n = 0;
64     XtSetArg(args[n], XmNtitle, APP_NAME); n++;
65     window->window = XtAppCreateShell(
66             APP_NAME,
67             APP_CLASS,
68             applicationShellWidgetClass,
69             //vendorShellWidgetClass,
70             display,
71             args,
72             n);
73     
74     
75     Atom wm_delete_window;
76     wm_delete_window = XmInternAtom(
77             display,
78             "WM_DELETE_WINDOW",
79             0);
80     XmAddWMProtocolCallback(
81             window->window,
82             wm_delete_window,
83             window_close_handler,
84             window);
85     
86     
87     n = 0;
88     XtSetArg(args[n], XmNwidth, 360); n++;
89     XtSetArg(args[n], XmNheight, 220); n++;
90     XtSetArg(args[n], XmNshadowThickness, 0); n++;
91     Widget container = XmCreateForm(window->window, "form", args, n);
92     XtManageChild(container);
93     XtAddEventHandler(container, KeyPressMask, FALSE, windowKeyEH, window);
94     
95     n = 0;
96     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
97     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
98     XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
99     WindowCreateMenu(window, container, args, n);
100     
101     n = 0;
102     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
103     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
104     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
105     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
106     XtSetArg(args[n], XmNtopWidget, window->menubar); n++;
107     XtSetArg(args[n], XmNbackground, BlackPixelOfScreen(XtScreen(window->window))); n++;
108     window->player_widget = XmCreateDrawingArea(container, "player", args, n);
109     XtManageChild(window->player_widget);
110     
111     // get F keycode
112     keycodeF = XKeysymToKeycode(XtDisplay(window->window), XStringToKeysym("F"));
113     
114     return window;
115 }
116
117 MainWindow* GetMainWindow(void) {
118     return main_window;
119 }
120
121 void WindowShow(MainWindow *win) {
122     XtRealizeWidget(win->window);
123 }
124
125 /*
126  * Creates a XmPushButton menu item
127  */
128 static Widget createMenuItem(
129         Widget menu,
130         char *name,
131         char *label,
132         char mnemonic,
133         const char *accelerator,
134         char *accelerator_text,
135         XtCallbackProc callback,
136         void *cbData)
137 {
138     Arg args[16];
139     int n = 0;
140     
141     XmString s1 = XmStringCreateSimple(label);
142     XtSetArg(args[n], XmNlabelString, s1); n++;
143     XtSetArg(args[n], XmNmnemonic, mnemonic); n++;
144     XmString at = NULL;
145     if(accelerator && accelerator_text) {
146         at = XmStringCreateSimple(accelerator_text);
147         XtSetArg(args[n], XmNaccelerator, accelerator); n++;
148         XtSetArg(args[n], XmNacceleratorText, at); n++;
149     }
150     
151     Widget menuItem = XmCreatePushButtonGadget(menu, name, args, n);
152     XtManageChild(menuItem);
153     XmStringFree(s1);
154     if(at) XmStringFree(at);
155     
156     if(callback) {
157         XtAddCallback(menuItem, XmNactivateCallback, (XtCallbackProc)callback, cbData);
158     }
159     
160     return menuItem;
161 }
162
163 static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nmbargs) {
164     Widget menubar = XmCreateMenuBar(parent, "menubar", mbargs, nmbargs);
165     XtManageChild(menubar);
166     win->menubar = menubar;
167     
168     Arg args[16];
169     int n;
170     
171     // menus
172     XmString s = XmStringCreateSimple("File");
173     Widget fileMenuItem = XtVaCreateManagedWidget(
174             "menuitem",
175             xmCascadeButtonWidgetClass,
176             menubar,
177             XmNlabelString, s,
178             NULL);
179     XmStringFree(s);
180     Widget fileMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 0, NULL, NULL);
181     
182     s = XmStringCreateSimple("Playback");
183     Widget playMenuItem = XtVaCreateManagedWidget(
184             "menuitem",
185             xmCascadeButtonWidgetClass,
186             menubar,
187             XmNlabelString, s,
188             NULL);
189     XmStringFree(s);
190     Widget playMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 1, NULL, NULL);
191     
192     s = XmStringCreateSimple("View");
193     Widget viewMenuItem = XtVaCreateManagedWidget(
194             "menuitem",
195             xmCascadeButtonWidgetClass,
196             menubar,
197             XmNlabelString, s,
198             NULL);
199     XmStringFree(s);
200     Widget viewMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 2, NULL, NULL);
201     
202     // file menu
203     createMenuItem(fileMenu, "fileOpen", "Open...", 'O', "Ctrl<Key>O", "Ctrl+O", FileOpenCB, NULL);
204     
205     // view menu
206     createMenuItem(viewMenu, "viewFullscreen", "Fullscreen", 'F', "<Key>F", "F", ViewFullscreenCB, NULL);
207 }
208
209 void go_fullscreen(Display *dsp, Window win)
210 {
211   XEvent xev;
212   Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False);
213   Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False);
214   memset(&xev, 0, sizeof(xev));
215   xev.type = ClientMessage;
216   xev.xclient.window = win;
217   xev.xclient.message_type = wm_state;
218   xev.xclient.format = 32;
219   xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
220   xev.xclient.data.l[1] = fullscreen;
221   xev.xclient.data.l[2] = 0;
222   XSendEvent(dsp, DefaultRootWindow(dsp), False,
223     SubstructureNotifyMask, &xev);
224 }
225
226 static Atom net_wm_state;
227 static Atom net_wm_state_fullscreen;
228 static int net_wm_atoms_initialized = 0;
229
230 void WindowFullscreen(MainWindow *win, bool enableFullscreen) {
231     Display *dpy = XtDisplay(win->window);
232     
233     // init net_wm_state atoms
234     if(!net_wm_atoms_initialized) {
235         net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
236         net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
237         net_wm_atoms_initialized = 1;
238     }
239     
240     WindowMenubarSetVisible(win, !enableFullscreen);
241     if(enableFullscreen && !win->fullscreen) {
242         XtUnmanageChild(main_window->menubar);
243         main_window->fullscreen = TRUE;
244     } else if(!enableFullscreen && win->fullscreen) {
245         XtManageChild(main_window->menubar);
246         main_window->fullscreen = FALSE;
247     }
248     
249     XEvent ev;
250     memset(&ev, 0, sizeof(XEvent));
251     ev.type = ClientMessage;
252     ev.xclient.window = XtWindow(win->window);
253     ev.xclient.message_type = net_wm_state;
254     ev.xclient.format = 32;
255     ev.xclient.data.l[0] = enableFullscreen ? 1 : 0;
256     ev.xclient.data.l[1] = net_wm_state_fullscreen;
257     ev.xclient.data.l[2] = 0;
258     XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask | SubstructureRedirectMask, &ev);
259 }
260
261 void WindowMenubarSetVisible(MainWindow *win, bool visible) {
262     if(visible) {
263         XtManageChild(main_window->menubar);
264         XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, main_window->menubar, NULL);
265     } else {
266         XtUnmanageChild(main_window->menubar);
267         XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_FORM, NULL);
268     }
269 }
270
271
272
273 static void filedialog_end(
274         Widget widget,
275         MainWindow *data,
276         XmFileSelectionBoxCallbackStruct *selection)
277 {
278     XtUnmanageChild(widget);
279     XtDestroyWidget(widget);
280 }
281
282 static void filedialog_select(
283         Widget widget,
284         MainWindow *data,
285         XmFileSelectionBoxCallbackStruct *selection)
286 {
287     char *value = NULL;
288     if(selection->value) {
289         XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value);
290         if(value) {
291             if(data->file) {
292                 XtFree(data->file);
293             }
294             data->file = value;
295             // no need to free the value, because it is stored in MainWindow
296             
297             PlayerOpenFile(data);
298         }
299     }
300     filedialog_end(widget, data, NULL);
301 }
302
303
304
305
306 static void FileOpenCB(Widget w, void *udata, void *cdata) {
307     MainWindow *win = main_window;
308     
309     Arg args[16];
310     int n = 0;
311     
312     XtSetArg(args[n], XnNshowViewMenu, 1); n++;
313     Widget dialog = XnCreateFileSelectionDialog(win->window, "dialog", args, n);
314     XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, win);
315     XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_end, win);
316     
317     Widget dirUp = XnFileSelectionBoxGetChild(dialog, XnFSB_DIR_UP_BUTTON);
318     XtUnmanageChild(dirUp);
319     
320     XtManageChild(dialog);
321 }
322
323 static void ViewFullscreenCB(Widget w, void *udata, void *cdata) {
324     if(main_window->fullscreen) {
325         WindowFullscreen(main_window, FALSE);
326     } else {
327         WindowFullscreen(main_window, TRUE);
328     }
329     
330 }