diff options
author | dec05eba <dec05eba@protonmail.com> | 2022-03-30 16:16:51 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2022-03-30 16:16:51 +0200 |
commit | 889efe51b26d505171d132097ac77a14ade5a101 (patch) | |
tree | 0a4233cdb4270a3c2ab54184f43b7344eb660a5b | |
parent | 44b986c762815874ccfb636152ae452ecba48764 (diff) |
Initial commit, showing ui above target window
-rw-r--r-- | .gitmodules | 3 | ||||
m--------- | depends/mglpp | 0 | ||||
-rw-r--r-- | fonts/Orbitron-Bold.ttf | bin | 0 -> 24308 bytes | |||
-rw-r--r-- | fonts/Orbitron-Regular.ttf | bin | 0 -> 24368 bytes | |||
-rw-r--r-- | images/record.png | bin | 0 -> 4196 bytes | |||
-rw-r--r-- | images/replay.png | bin | 0 -> 5221 bytes | |||
-rw-r--r-- | images/stream.png | bin | 0 -> 5235 bytes | |||
-rw-r--r-- | include/gui/Button.hpp | 16 | ||||
-rw-r--r-- | include/gui/Widget.hpp | 21 | ||||
-rw-r--r-- | main.cpp | 71 | ||||
-rw-r--r-- | project.conf | 5 | ||||
-rw-r--r-- | src/gui/Button.cpp | 71 | ||||
-rw-r--r-- | src/gui/Widget.cpp | 7 | ||||
-rw-r--r-- | src/main.cpp | 276 |
14 files changed, 397 insertions, 73 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d0e51e3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "depends/mglpp"] + path = depends/mglpp + url = https://repo.dec05eba.com/mglpp diff --git a/depends/mglpp b/depends/mglpp new file mode 160000 +Subproject 2c879c34c9aeddae528d2818f7baa6a915d5d77 diff --git a/fonts/Orbitron-Bold.ttf b/fonts/Orbitron-Bold.ttf Binary files differnew file mode 100644 index 0000000..faca54c --- /dev/null +++ b/fonts/Orbitron-Bold.ttf diff --git a/fonts/Orbitron-Regular.ttf b/fonts/Orbitron-Regular.ttf Binary files differnew file mode 100644 index 0000000..53260a0 --- /dev/null +++ b/fonts/Orbitron-Regular.ttf diff --git a/images/record.png b/images/record.png Binary files differnew file mode 100644 index 0000000..92c2bcb --- /dev/null +++ b/images/record.png diff --git a/images/replay.png b/images/replay.png Binary files differnew file mode 100644 index 0000000..65c9339 --- /dev/null +++ b/images/replay.png diff --git a/images/stream.png b/images/stream.png Binary files differnew file mode 100644 index 0000000..970a673 --- /dev/null +++ b/images/stream.png diff --git a/include/gui/Button.hpp b/include/gui/Button.hpp new file mode 100644 index 0000000..ac9aa5c --- /dev/null +++ b/include/gui/Button.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "Widget.hpp" +#include <string> + +namespace gsr { + class Button : public Widget { + public: + Button(mgl::vec2f size); + void on_event(mgl::Event &event, mgl::Window &window) override; + void draw(mgl::Window &window) override; + private: + mgl::vec2f size; + bool mouse_inside = false; + }; +}
\ No newline at end of file diff --git a/include/gui/Widget.hpp b/include/gui/Widget.hpp new file mode 100644 index 0000000..cf81d69 --- /dev/null +++ b/include/gui/Widget.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <mglpp/system/vec.hpp> + +namespace mgl { + class Event; + class Window; +} + +namespace gsr { + class Widget { + public: + virtual ~Widget() = default; + + virtual void on_event(mgl::Event &event, mgl::Window &window) = 0; + virtual void draw(mgl::Window &window) = 0; + virtual void set_position(mgl::vec2f position); + protected: + mgl::vec2f position; + }; +}
\ No newline at end of file diff --git a/main.cpp b/main.cpp deleted file mode 100644 index ee58e4d..0000000 --- a/main.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <X11/Xlib.h> -#include <X11/cursorfont.h> -#include <unistd.h> - -static int x11_error_handler(Display *dpy, XErrorEvent *ev) { - return 0; -} - -static void usage() { - fprintf(stderr, "usage: window-overlay <window>\n"); - exit(1); -} - -static bool string_to_i64(const char *str, int64_t *result) { - errno = 0; - char *endptr = nullptr; - int64_t res = strtoll(str, &endptr, 0); - if(endptr == str || errno != 0) - return false; - - *result = res; - return true; -} - -int main(int argc, char **argv) { - if(argc != 2) - usage(); - - int64_t target_window; - if(!string_to_i64(argv[1], &target_window)) { - fprintf(stderr, "Error: invalid number '%s' was specific for window argument\n", argv[1]); - return 1; - } - - XSetErrorHandler(x11_error_handler); - - Display *display = XOpenDisplay(NULL); - if(!display) { - fprintf(stderr, "Error: XOpenDisplay failed\n"); - return 1; - } - - //const Window root_window = DefaultRootWindow(display); - const int screen = DefaultScreen(display); - - XSetWindowAttributes attr; - attr.background_pixel = XWhitePixel(display, screen); - attr.override_redirect = True; - - Window overlay_window = XCreateWindow(display, target_window, 0, 0, 256, 256, 0, DefaultDepth(display, screen), InputOutput, XDefaultVisual(display, screen), CWBackPixel|CWOverrideRedirect, &attr); - if(!overlay_window) { - fprintf(stderr, "Error: failed to create overlay window\n"); - return 1; - } - - Cursor default_cursor = XCreateFontCursor(display, XC_arrow); - - XMapWindow(display, overlay_window); - XGrabPointer(display, overlay_window, True, ButtonPressMask|ButtonReleaseMask, GrabModeSync, GrabModeSync, None, default_cursor, CurrentTime); - sleep(3); - - /*XEvent xev; - for(;;) { - XNextEvent(display, &xev); - }*/ - - return 0; -} diff --git a/project.conf b/project.conf index 2805290..f767638 100644 --- a/project.conf +++ b/project.conf @@ -1,8 +1,9 @@ [package] -name = "window-overlay" +name = "gpu-screen-recorder-overlay" type = "executable" version = "0.1.0" platforms = ["posix"] [dependencies] -x11 = ">=1" +x11 = "1" +xext = "1"
\ No newline at end of file diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp new file mode 100644 index 0000000..b266639 --- /dev/null +++ b/src/gui/Button.cpp @@ -0,0 +1,71 @@ +#include "../../include/gui/Button.hpp" +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/window/Event.hpp> +#include <mglpp/system/FloatRect.hpp> + +namespace gsr { + Button::Button(mgl::vec2f size) : size(size) { + + } + + void Button::on_event(mgl::Event &event, mgl::Window&) { + if(event.type == mgl::Event::MouseMoved) { + const bool inside = mgl::FloatRect(position, size).contains({ (float)event.mouse_move.x, (float)event.mouse_move.y }); + if(mouse_inside && !inside) { + mouse_inside = false; + } else if(!mouse_inside && inside) { + mouse_inside = true; + } + } + } + + void Button::draw(mgl::Window &window) { + if(mouse_inside) { + // Background + /* + { + mgl::Rectangle rect(size); + rect.set_position(position); + rect.set_color(mgl::Color(20, 20, 20, 255)); + window.draw(rect); + } + */ + + const int border_size = 5; + const mgl::Color border_color(118, 185, 0); + + // Green line at top + { + mgl::Rectangle rect({ size.x, border_size }); + rect.set_position(position); + rect.set_color(border_color); + window.draw(rect); + } + + // Green line at bottom + { + mgl::Rectangle rect({ size.x, border_size }); + rect.set_position(position + mgl::vec2f(0.0f, size.y - border_size)); + rect.set_color(border_color); + window.draw(rect); + } + + // Green line at left + { + mgl::Rectangle rect({ border_size, size.y }); + rect.set_position(position); + rect.set_color(border_color); + window.draw(rect); + } + + // Green line at right + { + mgl::Rectangle rect({ border_size, size.y }); + rect.set_position(position + mgl::vec2f(size.x - border_size, 0.0f)); + rect.set_color(border_color); + window.draw(rect); + } + } + } +}
\ No newline at end of file diff --git a/src/gui/Widget.cpp b/src/gui/Widget.cpp new file mode 100644 index 0000000..c30c1d1 --- /dev/null +++ b/src/gui/Widget.cpp @@ -0,0 +1,7 @@ +#include "../../include/gui/Widget.hpp" + +namespace gsr { + void Widget::set_position(mgl::vec2f position) { + this->position = position; + } +}
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..537011c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,276 @@ +#include "../include/gui/Button.hpp" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <X11/Xlib.h> +#include <X11/cursorfont.h> +#include <X11/extensions/shape.h> + +#include <mglpp/mglpp.hpp> +#include <mglpp/graphics/Font.hpp> +#include <mglpp/graphics/Text.hpp> +#include <mglpp/graphics/Texture.hpp> +#include <mglpp/graphics/Sprite.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/window/Event.hpp> +#include <mglpp/system/MemoryMappedFile.hpp> + +#include <vector> + +extern "C" { +#include <mgl/mgl.h> +} + +static void usage() { + fprintf(stderr, "usage: window-overlay <window>\n"); + exit(1); +} + +static void startup_error(const char *msg) { + fprintf(stderr, "Error: %s\n", msg); + exit(1); +} + +static bool string_to_i64(const char *str, int64_t *result) { + errno = 0; + char *endptr = nullptr; + int64_t res = strtoll(str, &endptr, 0); + if(endptr == str || errno != 0) + return false; + + *result = res; + return true; +} + +int main(int argc, char **argv) { + if(argc != 2) + usage(); + + int64_t target_window; + if(!string_to_i64(argv[1], &target_window)) { + fprintf(stderr, "Error: invalid number '%s' was specific for window argument\n", argv[1]); + return 1; + } + + mgl::Init init; + Display *display = (Display*)mgl_get_context()->connection; + + XWindowAttributes target_win_attr; + memset(&target_win_attr, 0, sizeof(target_win_attr)); + if(!XGetWindowAttributes(display, target_window, &target_win_attr)) { + fprintf(stderr, "Error: window argument %s is not a valid window\n", argv[1]); + return 1; + } + + XSelectInput(display, target_window, StructureNotifyMask); + + mgl::vec2i target_window_size = { target_win_attr.width, target_win_attr.height }; + + mgl::Window::CreateParams window_create_params; + window_create_params.size = target_window_size; + window_create_params.parent_window = target_window; + window_create_params.hidden = true; + + mgl::Window window; + if(!window.create("mglpp", window_create_params)) + startup_error("failed to create window"); + + mgl::MemoryMappedFile title_font_file; + if(!title_font_file.load("fonts/Orbitron-Bold.ttf", mgl::MemoryMappedFile::LoadOptions{true, false})) + startup_error("failed to load file: fonts/Orbitron-Bold.ttf"); + + mgl::MemoryMappedFile font_file; + if(!font_file.load("fonts/Orbitron-Regular.ttf", mgl::MemoryMappedFile::LoadOptions{true, false})) + startup_error("failed to load file: fonts/Orbitron-Regular.ttf"); + + mgl::Font title_font; + if(!title_font.load_from_file(title_font_file, 32)) + startup_error("failed to load font: fonts/Orbitron-Bold.ttf"); + + mgl::Font font; + if(!font.load_from_file(font_file, 20)) + startup_error("failed to load font: fonts/Orbitron-Regular.ttf"); + + mgl::Texture replay_button_texture; + if(!replay_button_texture.load_from_file("images/replay.png")) + startup_error("failed to load texture: images/replay.png"); + + mgl::Texture record_button_texture; + if(!record_button_texture.load_from_file("images/record.png")) + startup_error("failed to load texture: images/record.png"); + + mgl::Texture stream_button_texture; + if(!stream_button_texture.load_from_file("images/stream.png")) + startup_error("failed to load texture: images/stream.png"); + + struct MainButton { + mgl::Text title; + mgl::Text description; + mgl::Sprite icon; + gsr::Button button; + }; + + const char *titles[] = { + "Instant Replay", + "Record", + "Livestream" + }; + + const char *descriptions[] = { + "Off", + "Not recording", + "Not streaming" + }; + + mgl::Texture *textures[] = { + &replay_button_texture, + &record_button_texture, + &stream_button_texture + }; + + std::vector<MainButton> main_buttons; + std::vector<XRectangle> shapes; + + for(int i = 0; i < 3; ++i) { + mgl::Text title(titles[i], {0.0f, 0.0f}, title_font); + title.set_color(mgl::Color(255, 255, 255)); + + mgl::Text description(descriptions[i], {0.0f, 0.0f}, font); + description.set_color(mgl::Color(255, 255, 255, 150)); + + mgl::Sprite sprite(textures[i]); + gsr::Button button({425, 300}); + + MainButton main_button = { + std::move(title), + std::move(description), + std::move(sprite), + std::move(button) + }; + + main_buttons.push_back(std::move(main_button)); + shapes.push_back({}); + } + + // Settings button + shapes.push_back({}); + + const int per_button_width = 425; + const mgl::vec2i overlay_desired_size = { per_button_width * (int)main_buttons.size(), 300 }; + + XGCValues xgcv; + xgcv.foreground = WhitePixel(display, DefaultScreen(display)); + xgcv.line_width = 1; + xgcv.line_style = LineSolid; + + Pixmap shape_pixmap = None; + GC shape_gc = None; + + auto update_overlay_shape = [&]() { + const int main_button_margin = 20; + const mgl::vec2i main_buttons_start_pos = target_window_size/2 - overlay_desired_size/2; + mgl::vec2i main_button_pos = main_buttons_start_pos; + + for(size_t i = 0; i < main_buttons.size(); ++i) { + main_buttons[i].title.set_position( + mgl::vec2f( + main_button_pos.x + per_button_width * 0.5f - main_buttons[i].title.get_bounds().size.x * 0.5f, + main_button_pos.y + main_button_margin).floor()); + + main_buttons[i].description.set_position( + mgl::vec2f( + main_button_pos.x + per_button_width * 0.5f - main_buttons[i].description.get_bounds().size.x * 0.5f, + main_button_pos.y + overlay_desired_size.y - main_buttons[i].description.get_bounds().size.y - main_button_margin).floor()); + + main_buttons[i].icon.set_position( + mgl::vec2f( + main_button_pos.x + per_button_width * 0.5f - main_buttons[i].icon.get_texture()->get_size().x * 0.5f, + main_button_pos.y + overlay_desired_size.y * 0.5f - main_buttons[i].icon.get_texture()->get_size().y * 0.5f).floor()); + + main_buttons[i].button.set_position(main_button_pos.to_vec2f()); + shapes[i] = { + (short)main_button_pos.x, (short)main_button_pos.y, (unsigned short)per_button_width, (unsigned short)overlay_desired_size.y + }; + main_button_pos.x += per_button_width; + } + + const mgl::vec2i settings_button_size(128, 128); + shapes[main_buttons.size()] = { + (short)(main_buttons_start_pos.x + overlay_desired_size.x), (short)(main_buttons_start_pos.y - settings_button_size.y), (unsigned short)settings_button_size.x, (unsigned short)settings_button_size.y + }; + + if(shape_pixmap) { + XFreePixmap(display, shape_pixmap); + shape_pixmap = None; + } + + if(shape_gc) { + XFreeGC(display, shape_gc); + shape_gc = None; + } + + shape_pixmap = XCreatePixmap(display, window.get_system_handle(), target_window_size.x, target_window_size.y, 1); + if(!shape_pixmap) + fprintf(stderr, "Error: failed to create shape pixmap\n"); + + shape_gc = XCreateGC(display, shape_pixmap, 0, &xgcv); + + XSetForeground(display, shape_gc, 0); + XFillRectangle(display, shape_pixmap, shape_gc, 0, 0, target_window_size.x, target_window_size.y); + + XSetForeground(display, shape_gc, 1); + XDrawRectangles(display, shape_pixmap, shape_gc, shapes.data(), shapes.size()); + XFillRectangles(display, shape_pixmap, shape_gc, shapes.data(), shapes.size()); + + XShapeCombineMask(display, window.get_system_handle(), ShapeBounding, 0, 0, shape_pixmap, ShapeSet); + }; + + update_overlay_shape(); + window.set_visible(true); + + Cursor default_cursor = XCreateFontCursor(display, XC_arrow); + + // TODO: Retry if these fail + XGrabPointer(display, window.get_system_handle(), True, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, default_cursor, CurrentTime); + XGrabKeyboard(display, window.get_system_handle(), True, GrabModeAsync, GrabModeAsync, CurrentTime); + + XEvent xev; + mgl::Event event; + + while(window.is_open()) { + if(XCheckTypedWindowEvent(display, target_window, ConfigureNotify, &xev) && (xev.xconfigure.width != target_window_size.x || xev.xconfigure.height != target_window_size.y)) { + while(XCheckTypedWindowEvent(display, target_window, ConfigureNotify, &xev)) {} + target_window_size.x = xev.xconfigure.width; + target_window_size.y = xev.xconfigure.height; + window.set_size(target_window_size); + update_overlay_shape(); + } + + if(window.poll_event(event)) { + for(auto &main_button : main_buttons) { + main_button.button.on_event(event, window); + } + + if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Escape) { + window.close(); + break; + } + } + } + + window.clear(mgl::Color(37, 43, 47, 255)); + for(auto &main_button : main_buttons) { + main_button.button.draw(window); + window.draw(main_button.icon); + window.draw(main_button.title); + window.draw(main_button.description); + } + window.display(); + } + + return 0; +} |