force window aspect ratio
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Fri, 14 Jan 2022 10:39:47 +0000 (11:39 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Fri, 14 Jan 2022 10:39:47 +0000 (11:39 +0100)
application/main.c
application/main.h
application/player.c
application/settings.c
application/window.c
application/window.h

index 55a9d4f..6aaac89 100644 (file)
@@ -38,6 +38,8 @@ static XtAppContext app;
 static Display *display;
 static Widget toplevel_window;
 
+static int event_pipe[2];
+
 static String fallback[] = {
         "*renderTable: rt",
         "*rt*fontType: FONT_IS_XFT",
@@ -66,10 +68,30 @@ static String langProc(Display *dp, String xnl, XtPointer closure) {
     return setlocale(LC_ALL, NULL);
 }
 
+typedef struct EventLoopCB {
+    XtWorkProc proc;
+    XtPointer data;
+} EventLoopCB;
+
+static void input_proc(XtPointer data, int *source, XtInputId *iid) {
+    EventLoopCB cb[16];
+    ssize_t r = read(event_pipe[0], cb, sizeof(EventLoopCB)*16);
+    size_t n = r / sizeof(EventLoopCB);
+    for(int i=0;i<n;i++) {
+        cb[i].proc(cb[i].data);
+    }
+}
+
 int main(int argc, char** argv) {  
     // disable stdout buffering, because the netbeans's internal terminal
     // has a bug on freebsd and doesn't flush the output after a newline
     setvbuf(stdout, NULL, _IONBF, 0);
+    
+    // init event pipe for xt event loop
+    if(pipe(event_pipe)) {
+        perror("pipe");
+        return 2;
+    }
      
     // initialize toolkit
     XtToolkitInitialize();
@@ -79,6 +101,13 @@ int main(int argc, char** argv) {
        
     display =  XtOpenDisplay(app, NULL, APP_NAME, APP_CLASS, NULL, 0, &argc, argv);
     
+    XtAppAddInput(
+            app,
+            event_pipe[0],
+            (XtPointer)XtInputReadMask,
+            input_proc,
+            NULL);
+    
     // load settings
     if(load_settings()) {
         return 1;
@@ -104,18 +133,9 @@ void ApplicationExit(void) {
     XtAppSetExitFlag(app);
 }
 
-void AppAddTimeOut(unsigned long interval, XtTimerCallbackProc proc, XtPointer data) {
-    XtAppAddTimeOut(app, interval, proc, data);
-    
-    if(!toplevel_window) return;
-    
-    // send a dummy X11 event, because the event loop may be waiting
-    // and the timeout proc is only called when an event is processed
-    XClientMessageEvent event;
-    memset(&event, 0, sizeof(XClientMessageEvent));
-    event.type = ClientMessage;
-    event.window = XtWindow(toplevel_window);
-    event.format = 32;
-    XSendEvent(display, XtWindow(toplevel_window), 0, 0, (XEvent*)&event);
-    XFlush(display);
+void AppExecProc(XtWorkProc proc, XtPointer data) {
+    EventLoopCB cb;
+    cb.proc = proc;
+    cb.data = data;
+    write(event_pipe[1], &cb, sizeof(cb));
 }
index 7a233c2..4198772 100644 (file)
@@ -36,7 +36,7 @@ XtAppContext* GetAppContext(void);
 
 void ApplicationExit(void);
 
-void AppAddTimeOut(unsigned long interval, XtTimerCallbackProc proc, XtPointer data);
+void AppExecProc(XtWorkProc proc, XtPointer data);
 
 
 #ifdef __cplusplus
index 5c481fa..7620a63 100644 (file)
@@ -309,7 +309,7 @@ static void player_io(Player *p) {
 
 static void handle_json_rpc_msg(Player *player, JSONValue *v) {
     if(v->type != JSON_OBJECT) return;
-    
+      
     JSONValue *request_id_v = json_obj_get(&v->value.object, "request_id");
     JSONValue *event = NULL;
     if(request_id_v && request_id_v->type == JSON_STRING) {
@@ -323,11 +323,11 @@ static void handle_json_rpc_msg(Player *player, JSONValue *v) {
         handle_json_rpc_event(player, v, event);
     }
     
-    json_print(v, NULL, 0);
+    //json_print(v, NULL, 0);
     fflush(stdout);
 }
 
-static void player_widget_set_size(XtPointer data, XtIntervalId *id) {
+static Boolean player_widget_set_size(XtPointer data) {
     Player *player = data;
     MainWindow *win = GetMainWindow();
         
@@ -339,8 +339,19 @@ static void player_widget_set_size(XtPointer data, XtIntervalId *id) {
     Dimension new_width = player->width + win_width - player_width;
     Dimension new_height = player->height + win_height - player_height;
     
+    // set window size
     XtVaSetValues(win->window, XmNwidth, new_width, XmNheight, new_height, NULL);
     
+    // set window aspect ratio
+    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);
+    
+    return 0;
 }
 
 
@@ -353,7 +364,7 @@ static void player_set_size(Player *player, int width, int height) {
         player->height = height;
     }
     if(player->width > 0 && player->height > 0) {
-        AppAddTimeOut(0, player_widget_set_size, player);
+        AppExecProc(player_widget_set_size, player);
     }
 }
 
@@ -392,13 +403,14 @@ static void handle_json_rpc_event(Player *p, JSONValue *v, JSONValue *event) {
                 PlayerEOF(p);
             }
         }
-    } else if(!json_strcmp(event, "playback-restart")) {
+    } else if(!p->isstarted && !json_strcmp(event, "playback-restart")) {
         char *cmd = "{ \"command\": [\"observe_property\", 1, \"playback-time\"] }\n"
                     "{ \"command\": [\"observe_property\", 1, \"eof-reached\"] }\n"
                     "{ \"command\": [\"get_property\", \"width\"], request_id=\"" REQ_ID_WIDTH "\" }\n"
                     "{ \"command\": [\"get_property\", \"height\"], request_id=\"" REQ_ID_HEIGHT "\" }\n"
                     "{ \"command\": [\"set_property\", \"keep-open\", true] }\n";
         write(p->ipc, cmd, strlen(cmd));
+        p->isstarted = TRUE;
     }
 }
 
index 22793b4..ceff1c7 100644 (file)
@@ -135,11 +135,12 @@ static char* get_which_output(FILE *f, UcxBuffer *buf) {
     return NULL;
 }
 
-static void finish_bin_search(XtPointer data, XtIntervalId *id) {
+static Boolean finish_bin_search(XtPointer data) {
     PlayerInfo *playerInfo = data;
     ucx_map_cstr_put(uwp_settings, UWP_PLAYER_BIN, playerInfo->bin);
     ucx_map_cstr_put(uwp_settings, UWP_PLAYER_TYPE, playerInfo->type);
     free(playerInfo);
+    return 0;
 }
 
 static void* player_bin_search_thread(void *data) {
@@ -152,7 +153,7 @@ static void* player_bin_search_thread(void *data) {
             PlayerInfo *playerInfo = malloc(sizeof(PlayerInfo));
             playerInfo->bin = strdup(bin);
             playerInfo->type = strdup("mpv");
-            AppAddTimeOut(0, finish_bin_search, playerInfo);
+            AppExecProc(finish_bin_search, playerInfo);
             
             ucx_buffer_free(buf);
             return NULL;
@@ -166,7 +167,7 @@ static void* player_bin_search_thread(void *data) {
             PlayerInfo *playerInfo = malloc(sizeof(PlayerInfo));
             playerInfo->bin = strdup(bin);
             playerInfo->type = strdup("mplayer");
-            AppAddTimeOut(0, finish_bin_search, playerInfo);
+            AppExecProc(finish_bin_search, playerInfo);
         }
     }
     
index 9ec4323..e5fc815 100644 (file)
@@ -51,6 +51,10 @@ static void windowKeyEH(Widget widget, XtPointer data, XEvent *event, Boolean *d
     }
 }
 
+static void resizeEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
+    WindowAdjustAspectRatio(data);
+}
+
 MainWindow* WindowCreate(Display *display) {
     Arg args[32];
     int n;
@@ -71,7 +75,7 @@ MainWindow* WindowCreate(Display *display) {
             args,
             n);
     
-    
+    // close handler
     Atom wm_delete_window;
     wm_delete_window = XmInternAtom(
             display,
@@ -83,7 +87,9 @@ MainWindow* WindowCreate(Display *display) {
             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++;
@@ -107,6 +113,7 @@ MainWindow* WindowCreate(Display *display) {
     XtSetArg(args[n], XmNbackground, BlackPixelOfScreen(XtScreen(window->window))); n++;
     window->player_widget = XmCreateDrawingArea(container, "player", args, n);
     XtManageChild(window->player_widget);
+    XmProcessTraversal(window->player_widget, XmTRAVERSE_CURRENT);
     
     // get F keycode
     keycodeF = XKeysymToKeycode(XtDisplay(window->window), XStringToKeysym("F"));
@@ -328,3 +335,32 @@ static void ViewFullscreenCB(Widget w, void *udata, void *cdata) {
     }
     
 }
+
+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);
+}
index 9ffa93c..d3e8623 100644 (file)
@@ -39,6 +39,7 @@ typedef struct Player {
     int ipc;
     int status;
     bool isactive;
+    bool isstarted;
     
     double playback_time;
     int width;
@@ -65,6 +66,8 @@ void WindowFullscreen(MainWindow *win, bool enableFullscreen);
 
 void WindowMenubarSetVisible(MainWindow *win, bool visible);
 
+void WindowAdjustAspectRatio(MainWindow *win);
+
 #ifdef __cplusplus
 }
 #endif