From d3543b4f6d654cae1d1d9ffe1f640106dad5cfed Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 6 Apr 2021 22:54:12 +0200 Subject: Add support for embedding the window into another application, such as suckless tabbed --- src/QuickMedia.cpp | 245 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 143 insertions(+), 102 deletions(-) (limited to 'src/QuickMedia.cpp') diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index c7d63d7..afd8461 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -395,74 +395,16 @@ namespace QuickMedia { Program::Program() : disp(nullptr), - window(sf::VideoMode(1280, 720, 24), "QuickMedia", sf::Style::Default), window_size(1280, 720), current_page(PageType::EXIT), image_index(0), tab_background(sf::Vector2f(1.0f, 1.0f), 10.0f, 10) { - disp = XOpenDisplay(NULL); - if (!disp) - throw std::runtime_error("Failed to open display to X11 server"); - - resources_root = "/usr/share/quickmedia/"; - if(get_file_type("../../../images/manganelo_logo.png") == FileType::REGULAR) { - resources_root = "../../../"; - } - - set_resource_loader_root_path(resources_root.c_str()); - - if(!circle_mask_shader.loadFromFile(resources_root + "shaders/circle_mask.glsl", sf::Shader::Type::Fragment)) { - fprintf(stderr, "Failed to load %s/shaders/circle_mask.glsl", resources_root.c_str()); - abort(); - } - - if(!loading_icon.loadFromFile(resources_root + "images/loading_icon.png")) { - fprintf(stderr, "Failed to load %s/images/loading_icon.png", resources_root.c_str()); - abort(); - } - loading_icon.setSmooth(true); - load_sprite.setTexture(loading_icon, true); - sf::Vector2u loading_icon_size = loading_icon.getSize(); - load_sprite.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f); - - struct sigaction action; - action.sa_handler = sigpipe_handler; - sigemptyset(&action.sa_mask); - action.sa_flags = 0; - sigaction(SIGPIPE, &action, NULL); - - XSetErrorHandler(x_error_handler); - XSetIOErrorHandler(x_io_error_handler); - - window.setVerticalSyncEnabled(true); - monitor_hz = get_monitor_max_hz(disp); - window.setFramerateLimit(FPS_IDLE); - idle = true; - vsync_set = false; - /* - if(enable_vsync(disp, window.getSystemHandle())) { - vsync_set = true; - } else { - fprintf(stderr, "Failed to enable vsync, fallback to frame limiting\n"); - window.setFramerateLimit(monitor_hz); - } - */ - fprintf(stderr, "Monitor hz: %d\n", monitor_hz); - - if(create_directory_recursive(get_cache_dir().join("thumbnails")) != 0) { - fprintf(stderr, "Failed to create thumbnails directory\n"); - } - - const char *qm_phone_factor = getenv("QM_PHONE_FACTOR"); - if(qm_phone_factor && atoi(qm_phone_factor) == 1) - show_room_side_panel = false; - else - show_room_side_panel = true; - main_thread_id = std::this_thread::get_id(); + } Program::~Program() { + window.close(); images_to_upscale_queue.close(); if(image_upscale_thead.joinable()) image_upscale_thead.join(); @@ -473,18 +415,20 @@ namespace QuickMedia { } static void usage() { - fprintf(stderr, "usage: quickmedia [--no-video] [--use-system-mpv-config] [--dir ]\n"); + fprintf(stderr, "usage: quickmedia [--no-video] [--use-system-mpv-config] [--dir ] [-e ]\n"); fprintf(stderr, "OPTIONS:\n"); fprintf(stderr, " plugin The plugin to use. Should be either launcher, 4chan, manganelo, mangatown, mangadex, pornhub, youtube, spotify, soundcloud, nyaa.si, matrix, file-manager or stdin\n"); fprintf(stderr, " --no-video Only play audio when playing a video. Disabled by default\n"); fprintf(stderr, " --use-system-mpv-config Use system mpv config instead of no config. Disabled by default\n"); fprintf(stderr, " --upscale-images Upscale low-resolution manga pages using waifu2x-ncnn-vulkan. Disabled by default\n"); fprintf(stderr, " --upscale-images-always Upscale manga pages using waifu2x-ncnn-vulkan, no matter what the original image resolution is. Disabled by default\n"); - fprintf(stderr, " --dir Set the start directory when using file-manager\n"); + fprintf(stderr, " --dir Set the start directory when using file-manager\n"); + fprintf(stderr, " -e Embed QuickMedia into another window\n"); fprintf(stderr, "EXAMPLES:\n"); - fprintf(stderr, " quickmedia manganelo\n"); + fprintf(stderr, " quickmedia launcher\n"); fprintf(stderr, " quickmedia --upscale-images-always manganelo\n"); fprintf(stderr, " echo -e \"hello\\nworld\" | quickmedia stdin\n"); + fprintf(stderr, " tabbed quickmedia launcher -e\n"); } static bool is_manga_plugin(const char *plugin_name) { @@ -508,6 +452,7 @@ namespace QuickMedia { } const char *start_dir = nullptr; + Window parent_window = None; std::vector tabs; for(int i = 1; i < argc; ++i) { @@ -539,6 +484,20 @@ namespace QuickMedia { } } else if(strcmp(argv[i], "--low-cpu-mode") == 0) { low_cpu_mode = true; + } else if(strcmp(argv[i], "-e") == 0) { + if(i < argc - 1) { + parent_window = strtol(argv[i + 1], nullptr, 0); + if(parent_window == None && errno == EINVAL) { + fprintf(stderr, "Invalid -e argument. Argument has to be a number\n"); + usage(); + return -1; + } + ++i; + } else { + fprintf(stderr, "Missing window to embed into after -e argument\n"); + usage(); + return -1; + } } else if(argv[i][0] == '-') { fprintf(stderr, "Invalid option %s\n", argv[i]); usage(); @@ -546,17 +505,17 @@ namespace QuickMedia { } } - if(low_cpu_mode) - FPS_IDLE = 2; - else - FPS_IDLE = 20; - if(!plugin_name) { fprintf(stderr, "Missing plugin argument\n"); usage(); return -1; } + if(low_cpu_mode) + FPS_IDLE = 2; + else + FPS_IDLE = 20; + if(upscale_image_action != UpscaleImageAction::NO) { if(!is_manga_plugin(plugin_name)) { fprintf(stderr, "Option --upscale-images/-upscale-images-force is only valid for manganelo, mangatown and mangadex\n"); @@ -605,6 +564,7 @@ namespace QuickMedia { return -1; } + init(parent_window); load_plugin_by_name(tabs, start_dir); while(!tabs.empty() || matrix) { @@ -632,16 +592,99 @@ namespace QuickMedia { return exit_code; } + void Program::init(Window parent_window) { + disp = XOpenDisplay(NULL); + if (!disp) + throw std::runtime_error("Failed to open display to X11 server"); + + wm_delete_window_atom = XInternAtom(disp, "WM_DELETE_WINDOW", False); + + int screen = DefaultScreen(disp); + int screen_center_x = (DisplayWidth(disp, screen) - window_size.x) / 2; + int screen_center_y = (DisplayHeight(disp, screen) - window_size.y) / 2; + + x11_window = XCreateWindow(disp, parent_window ? parent_window : DefaultRootWindow(disp), + screen_center_x, screen_center_y, window_size.x, window_size.y, 0, + DefaultDepth(disp, screen), + InputOutput, + DefaultVisual(disp, screen), + 0, nullptr); + if(!x11_window) + throw std::runtime_error("Failed to create window"); + + XStoreName(disp, x11_window, "QuickMedia"); + XMapWindow(disp, x11_window); + XFlush(disp); + + window.create(x11_window); + + resources_root = "/usr/share/quickmedia/"; + if(get_file_type("../../../images/manganelo_logo.png") == FileType::REGULAR) { + resources_root = "../../../"; + } + + set_resource_loader_root_path(resources_root.c_str()); + + if(!circle_mask_shader.loadFromFile(resources_root + "shaders/circle_mask.glsl", sf::Shader::Type::Fragment)) { + fprintf(stderr, "Failed to load %s/shaders/circle_mask.glsl", resources_root.c_str()); + abort(); + } + + if(!loading_icon.loadFromFile(resources_root + "images/loading_icon.png")) { + fprintf(stderr, "Failed to load %s/images/loading_icon.png", resources_root.c_str()); + abort(); + } + loading_icon.setSmooth(true); + load_sprite.setTexture(loading_icon, true); + sf::Vector2u loading_icon_size = loading_icon.getSize(); + load_sprite.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f); + + struct sigaction action; + action.sa_handler = sigpipe_handler; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + sigaction(SIGPIPE, &action, NULL); + + XSetErrorHandler(x_error_handler); + XSetIOErrorHandler(x_io_error_handler); + + window.setVerticalSyncEnabled(true); + monitor_hz = get_monitor_max_hz(disp); + window.setFramerateLimit(FPS_IDLE); + idle = true; + vsync_set = false; + /* + if(enable_vsync(disp, window.getSystemHandle())) { + vsync_set = true; + } else { + fprintf(stderr, "Failed to enable vsync, fallback to frame limiting\n"); + window.setFramerateLimit(monitor_hz); + } + */ + fprintf(stderr, "Monitor hz: %d\n", monitor_hz); + + if(create_directory_recursive(get_cache_dir().join("thumbnails")) != 0) + throw std::runtime_error("Failed to create thumbnails directory"); + + const char *qm_phone_factor = getenv("QM_PHONE_FACTOR"); + if(qm_phone_factor && atoi(qm_phone_factor) == 1) + show_room_side_panel = false; + else + show_room_side_panel = true; + main_thread_id = std::this_thread::get_id(); + } + void Program::load_plugin_by_name(std::vector &tabs, const char *start_dir) { if(!plugin_name || plugin_name[0] == '\0') return; window.setTitle("QuickMedia - " + std::string(plugin_name)); - const char *plugin_logo_name = get_plugin_logo_name(plugin_name); + no_video = force_no_video; + std::string plugin_logo_path; + const char *plugin_logo_name = get_plugin_logo_name(plugin_name); if(plugin_logo_name) plugin_logo_path = resources_root + "images/" + plugin_logo_name; - no_video = force_no_video; plugin_logo = sf::Texture(); if(!plugin_logo_path.empty()) { @@ -744,11 +787,15 @@ namespace QuickMedia { } } - void Program::base_event_handler(sf::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_keypress, bool handle_searchbar) { - if (event.type == sf::Event::Closed) { + void Program::handle_window_close() { + if(wm_delete_window_atom && XCheckTypedWindowEvent(disp, x11_window, ClientMessage, &xev) && (Atom)xev.xclient.data.l[0] == wm_delete_window_atom) { current_page = PageType::EXIT; window.close(); - } else if(event.type == sf::Event::Resized) { + } + } + + void Program::base_event_handler(sf::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_keypress, bool handle_searchbar) { + if(event.type == sf::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); @@ -1087,14 +1134,13 @@ namespace QuickMedia { } void Program::page_loop(std::vector &tabs, int start_tab_index, PageLoopSubmitHandler after_submit_handler) { - malloc_trim(0); - if(tabs.empty()) { show_notification("QuickMedia", "No tabs provided!", Urgency::CRITICAL); return; } window.setFramerateLimit(FPS_IDLE); + malloc_trim(0); idle = true; for(Tab &tab : tabs) { @@ -1318,9 +1364,7 @@ namespace QuickMedia { else event_idle_handler(event); - if (event.type == sf::Event::Closed) { - window.close(); - } else if(event.type == sf::Event::Resized) { + if(event.type == sf::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); @@ -1416,6 +1460,7 @@ namespace QuickMedia { } } update_idle_state(); + handle_window_close(); if(redraw) { redraw = false; @@ -1711,12 +1756,7 @@ namespace QuickMedia { sf::Event event; while(window.isOpen()) { while(window.pollEvent(event)) { - if(event.type == sf::Event::Closed) { - task.cancel(); - current_page = PageType::EXIT; - window.close(); - return TaskResult::CANCEL; - } else if(event.type == sf::Event::Resized) { + if(event.type == sf::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); @@ -1727,6 +1767,13 @@ namespace QuickMedia { } } + if(wm_delete_window_atom && XCheckTypedWindowEvent(disp, x11_window, ClientMessage, &xev) && (Atom)xev.xclient.data.l[0] == wm_delete_window_atom) { + task.cancel(); + current_page = PageType::EXIT; + window.close(); + return TaskResult::CANCEL; + } + if(task.ready()) { if(task.get()) return TaskResult::TRUE; @@ -1953,10 +2000,7 @@ namespace QuickMedia { while (current_page == PageType::VIDEO_CONTENT && window.isOpen()) { while (window.pollEvent(event)) { - if (event.type == sf::Event::Closed) { - current_page = PageType::EXIT; - window.close(); - } else if(event.type == sf::Event::Resized) { + if(event.type == sf::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); @@ -1968,6 +2012,7 @@ namespace QuickMedia { save_video_url_to_clipboard(); } } + handle_window_close(); if(video_player_window && XCheckTypedWindowEvent(disp, video_player_window, KeyPress, &xev)/* && xev.xkey.subwindow == video_player_window*/) { #pragma GCC diagnostic push @@ -2448,10 +2493,7 @@ namespace QuickMedia { // TODO: Show to user if a certain page is missing (by checking page name (number) and checking if some are skipped) while (current_page == PageType::IMAGES && window.isOpen()) { while(window.pollEvent(event)) { - if (event.type == sf::Event::Closed) { - current_page = PageType::EXIT; - window.close(); - } else if(event.type == sf::Event::Resized) { + if(event.type == sf::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); @@ -2487,6 +2529,7 @@ namespace QuickMedia { } } } + handle_window_close(); if(download_in_progress && check_downloaded_timer.getElapsedTime().asMilliseconds() >= check_downloaded_timeout_ms) { sf::String error_msg; @@ -2604,7 +2647,7 @@ namespace QuickMedia { json_chapter = Json::Value(Json::objectValue); } - ImageViewer image_viewer(images_page, images_page->manga_name, images_page->get_chapter_name(), image_index, content_cache_dir); + ImageViewer image_viewer(&window, images_page, images_page->manga_name, images_page->get_chapter_name(), image_index, content_cache_dir); json_chapter["current"] = std::min(latest_read, image_viewer.get_num_pages()); json_chapter["total"] = image_viewer.get_num_pages(); @@ -2616,8 +2659,9 @@ namespace QuickMedia { window.setFramerateLimit(monitor_hz); while(current_page == PageType::IMAGES_CONTINUOUS && window.isOpen()) { + handle_window_close(); window.clear(back_color); - ImageViewerAction action = image_viewer.draw(window); + ImageViewerAction action = image_viewer.draw(); switch(action) { case ImageViewerAction::NONE: break; @@ -2850,10 +2894,7 @@ namespace QuickMedia { } } - if(event.type == sf::Event::Closed) { - current_page = PageType::EXIT; - window.close(); - } else if(event.type == sf::Event::Resized) { + if(event.type == sf::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); @@ -3016,6 +3057,7 @@ namespace QuickMedia { } frame_skip_text_entry = false; update_idle_state(); + handle_window_close(); chat_input_height_full = comment_input.get_height() + chat_input_padding_y * 2.0f; @@ -3195,10 +3237,7 @@ namespace QuickMedia { else event_idle_handler(event); - if (event.type == sf::Event::Closed) { - current_page = PageType::EXIT; - window.close(); - } else if(event.type == sf::Event::Resized) { + if(event.type == sf::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); @@ -3217,6 +3256,7 @@ namespace QuickMedia { inputs[focused_input]->on_event(event); } update_idle_state(); + handle_window_close(); if(redraw) { redraw = false; @@ -4690,6 +4730,7 @@ namespace QuickMedia { } frame_skip_text_entry = false; update_idle_state(); + handle_window_close(); matrix_chat_page->update(); -- cgit v1.2.3