diff --git a/data/freshwrapper.conf.example b/data/freshwrapper.conf.example index c7b27fb8..538c30f1 100644 --- a/data/freshwrapper.conf.example +++ b/data/freshwrapper.conf.example @@ -65,6 +65,10 @@ fullscreen_height = 0 fullscreen_horz_maximize_atom = 1 fullscreen_vert_maximize_atom = 1 +# When set, forces full screen window to have exact position and size. +# Separated by commas: left, top, width, height. +# fullscreen_window_geometry = "0,0,1920,1080" + # Enables DNS query case randomization to partially protect against DNS # poisoning attacks. It was reported that some Mikrotik routers do not # support this trick. Set parameter to 0 if you have an affected model diff --git a/src/config.c b/src/config.c index f7c4dca1..0801e5a2 100644 --- a/src/config.c +++ b/src/config.c @@ -48,6 +48,7 @@ static struct fpp_config_s default_config = { .fullscreen_height = 0, .fullscreen_horz_maximize_atom = 1, .fullscreen_vert_maximize_atom = 1, + .fullscreen_window_geometry = NULL, .randomize_dns_case = 0, .device_scale = 1.0, .enable_windowed_mode = 1, @@ -90,6 +91,7 @@ static cfg_opt_t opts[] = { CFG_SIMPLE_INT("fullscreen_height", &config.fullscreen_height), CFG_SIMPLE_INT("fullscreen_horz_maximize_atom", &config.fullscreen_horz_maximize_atom), CFG_SIMPLE_INT("fullscreen_vert_maximize_atom", &config.fullscreen_vert_maximize_atom), + CFG_SIMPLE_STR("fullscreen_window_geometry", &config.fullscreen_window_geometry), CFG_SIMPLE_INT("randomize_dns_case", &config.randomize_dns_case), CFG_SIMPLE_FLOAT("device_scale", &config.device_scale), CFG_SIMPLE_INT("enable_windowed_mode", &config.enable_windowed_mode), @@ -239,6 +241,7 @@ fpp_config_destroy(void) FREE_IF_CHANGED(pepperflash_path); FREE_IF_CHANGED(flash_command_line); FREE_IF_CHANGED(jack_server_name); + FREE_IF_CHANGED(fullscreen_window_geometry); g_free(pepper_data_dir); g_free(pepper_salt_file_name); initialized = 0; diff --git a/src/config.h b/src/config.h index fa8cb9d7..179f587e 100644 --- a/src/config.h +++ b/src/config.h @@ -40,6 +40,7 @@ struct fpp_config_s { int fullscreen_height; int fullscreen_horz_maximize_atom; int fullscreen_vert_maximize_atom; + char *fullscreen_window_geometry; int randomize_dns_case; double device_scale; int enable_windowed_mode; diff --git a/src/ppb_flash_fullscreen.c b/src/ppb_flash_fullscreen.c index 8611d463..2b97d716 100644 --- a/src/ppb_flash_fullscreen.c +++ b/src/ppb_flash_fullscreen.c @@ -223,6 +223,31 @@ craft_graphicsexpose_event(XEvent *ev, Display *dpy, struct pp_instance_s *pp_i) ev->xgraphicsexpose.height = pp_i->fs_height; } +static int +get_window_geometry_from_string(const char *str, struct PP_Rect *out) +{ + struct PP_Rect res = {}; + + gchar **parts = g_strsplit(str, ",", -1); + unsigned int n = 0; + for (gchar **ptr = parts; *ptr != NULL; ptr++) + n += 1; + + if (n < 4) { + g_strfreev(parts); + return -1; + } + + res.point.x = MIN(G_MAXINT32, g_ascii_strtoull(parts[0], NULL, 10)); + res.point.y = MIN(G_MAXINT32, g_ascii_strtoull(parts[1], NULL, 10)); + res.size.width = MIN(G_MAXINT32, g_ascii_strtoull(parts[2], NULL, 10)); + res.size.height = MIN(G_MAXINT32, g_ascii_strtoull(parts[3], NULL, 10)); + + g_strfreev(parts); + *out = res; + return 0; +} + static void fullscreen_window_thread_int(Display *dpy, struct thread_param_s *tp) @@ -240,15 +265,29 @@ fullscreen_window_thread_int(Display *dpy, struct thread_param_s *tp) // get current mouse pointer position XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child, &px, &py, &rel_x, &rel_y, &mask); - // create tiny window where mouse pointer is + // Create a window for the fullscreen content. By default, it's a small window just under + // mouse cursor. That hint a window manager on which monitor to expand the window. + struct PP_Rect fs_wnd_rect = { + .point = {.x = px - wnd_size / 2, .y = py - wnd_size / 2}, + .size = {.width = wnd_size, .height = wnd_size}, + }; + + if (config.fullscreen_window_geometry != NULL) + get_window_geometry_from_string(config.fullscreen_window_geometry, &fs_wnd_rect); + + printf("fs_wnd_rect: %d %d %d %d\n", fs_wnd_rect.point.x, + fs_wnd_rect.point.y, fs_wnd_rect.size.width, fs_wnd_rect.size.height); + XSetWindowAttributes attrs = { .background_pixel = 0x000000, .backing_store = Always, }; - pp_i->fs_wnd = XCreateWindow(dpy, DefaultRootWindow(dpy), - px - wnd_size / 2, py - wnd_size / 2, wnd_size, wnd_size, - 0, DefaultDepth(dpy, screen), InputOutput, - DefaultVisual(dpy, screen), CWBackPixel | CWBackingStore, &attrs); + + pp_i->fs_wnd = XCreateWindow( + dpy, DefaultRootWindow(dpy), fs_wnd_rect.point.x, fs_wnd_rect.point.y, + fs_wnd_rect.size.width, fs_wnd_rect.size.height, 0, + DefaultDepth(dpy, screen), InputOutput, DefaultVisual(dpy, screen), + CWBackPixel | CWBackingStore, &attrs); XSelectInput(dpy, pp_i->fs_wnd, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | @@ -257,8 +296,8 @@ fullscreen_window_thread_int(Display *dpy, struct thread_param_s *tp) // tell window manager we want exact position XSizeHints size_hints = { .flags = USPosition, - .x = px - wnd_size / 2, - .y = py - wnd_size / 2, + .x = fs_wnd_rect.point.x, + .y = fs_wnd_rect.point.y, }; XSetWMNormalHints(dpy, pp_i->fs_wnd, &size_hints); @@ -338,8 +377,8 @@ fullscreen_window_thread_int(Display *dpy, struct thread_param_s *tp) pthread_mutex_lock(&display.lock); pp_i->is_fullscreen = 1; - pp_i->fs_width_current = wnd_size; - pp_i->fs_height_current = wnd_size; + pp_i->fs_width_current = fs_wnd_rect.size.width; + pp_i->fs_height_current = fs_wnd_rect.size.height; pthread_mutex_unlock(&display.lock); int called_did_change_view = 0;