X-Git-Url: https://develop.uap-core.de/gitweb/uwplayer.git/blobdiff_plain/1a310acf08a2d12cba26c745c0d29030238675f0..01d5015ba093f8c5fdb18b669943c7da6450e72f:/application/window.c diff --git a/application/window.c b/application/window.c index da0b04c..96a4510 100644 --- a/application/window.c +++ b/application/window.c @@ -27,19 +27,50 @@ #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) { - WindowClosePlayer(main_window); - ApplicationExit(); + FileQuitCB(window, NULL, NULL); } static unsigned int keycodeF; @@ -52,10 +83,33 @@ static void windowKeyEH(Widget widget, XtPointer data, XEvent *event, Boolean *d } } -static void resizeEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { +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; @@ -65,40 +119,51 @@ static void playerWidgetInputCB(Widget widget, XtPointer u, XtPointer c) { } } -static void playerEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { - MainWindow *win = data; - if(!win->player || win->player->window == 0) return; - - /* - if(event->type == EnterNotify) { - printf("enter: grab pointer\n"); - - XtGrabPointer( +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, - CurrentTime); - + 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(event->type == LeaveNotify) { - printf("leave\n"); - XtUngrabPointer(win->player_widget, CurrentTime); + if(etype == LeaveNotify) { + //printf("leave\n"); + //XtUngrabButton(win->player_widget, AnyButton, AnyModifier); + //win->buttongrab = False; return; } - if(event->type == MotionNotify) { - static int testv = 0; - printf("test %d\n", testv++); + int pass = 0; + if(etype == ButtonPress || etype == ButtonRelease || etype == KeyPress || etype == KeyRelease) { + //printf("button press\n"); + pass = 1; } - */ - if(event->type == KeyPress || event->type == KeyRelease) { + 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), @@ -109,14 +174,94 @@ static void playerEH(Widget widget, XtPointer data, XEvent *event, Boolean *disp } } +#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++; @@ -143,7 +288,7 @@ MainWindow* WindowCreate(Display *display) { // resize handler XtAddEventHandler(window->window, StructureNotifyMask, False, resizeEH, window); - + n = 0; XtSetArg(args[n], XmNwidth, 360); n++; XtSetArg(args[n], XmNheight, 220); n++; @@ -159,8 +304,25 @@ MainWindow* WindowCreate(Display *display) { 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++; @@ -173,10 +335,15 @@ MainWindow* WindowCreate(Display *display) { 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; } @@ -226,6 +393,55 @@ static Widget createMenuItem( 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); @@ -236,7 +452,7 @@ static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nm // menus XmString s = XmStringCreateSimple("File"); - Widget fileMenuItem = XtVaCreateManagedWidget( + XtVaCreateManagedWidget( "menuitem", xmCascadeButtonWidgetClass, menubar, @@ -246,7 +462,7 @@ static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nm Widget fileMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 0, NULL, NULL); s = XmStringCreateSimple("Playback"); - Widget playMenuItem = XtVaCreateManagedWidget( + XtVaCreateManagedWidget( "menuitem", xmCascadeButtonWidgetClass, menubar, @@ -265,11 +481,40 @@ static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nm 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) @@ -312,6 +557,8 @@ void WindowFullscreen(MainWindow *win, bool enableFullscreen) { main_window->fullscreen = FALSE; } + WindowShowSidebar(win, enableFullscreen ? false : win->sidebarvisible); + XEvent ev; memset(&ev, 0, sizeof(XEvent)); ev.type = ClientMessage; @@ -334,8 +581,6 @@ void WindowMenubarSetVisible(MainWindow *win, bool visible) { } } - - static void filedialog_end( Widget widget, MainWindow *data, @@ -354,13 +599,9 @@ static void filedialog_select( if(selection->value) { XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value); if(value) { - if(data->file) { - XtFree(data->file); - } - data->file = value; - // no need to free the value, because it is stored in MainWindow - - PlayerOpenFile(data); + PlayListAddFile(data, value); + PlayListPlayNext(data, true); + XtFree(value); } } filedialog_end(widget, data, NULL); @@ -386,19 +627,101 @@ static void FileOpenCB(Widget w, void *udata, void *cdata) { 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) @@ -429,4 +752,35 @@ void WindowClosePlayer(MainWindow *win) { 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); }