aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--include/GsrInfo.hpp66
-rw-r--r--include/Theme.hpp18
-rw-r--r--include/gui/DropdownButton.hpp54
-rw-r--r--include/gui/Utils.hpp13
-rw-r--r--src/GsrInfo.cpp179
-rw-r--r--src/Theme.cpp34
-rw-r--r--src/gui/Button.cpp3
-rw-r--r--src/gui/ComboBox.cpp44
-rw-r--r--src/gui/DropdownButton.cpp181
-rw-r--r--src/gui/Utils.cpp43
-rw-r--r--src/gui/WidgetContainer.cpp5
-rw-r--r--src/main.cpp104
13 files changed, 644 insertions, 102 deletions
diff --git a/TODO b/TODO
index bff916d..30df401 100644
--- a/TODO
+++ b/TODO
@@ -9,3 +9,5 @@ Maybe change design to have black triangles appear and get larger until they fil
All of these things should be done with vertex buffer, for real 3D.
DISPLAY gamescope-0
+
+Colorscheme should follow graphics card in use. On nvidia use nvidia green, on intel use intel blue and on amd use amd red. \ No newline at end of file
diff --git a/include/GsrInfo.hpp b/include/GsrInfo.hpp
new file mode 100644
index 0000000..e029919
--- /dev/null
+++ b/include/GsrInfo.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <mglpp/system/vec.hpp>
+
+namespace gsr {
+ struct SupportedVideoCodecs {
+ bool h264 = false;
+ bool hevc = false;
+ bool av1 = false;
+ bool vp8 = false;
+ bool vp9 = false;
+ };
+
+ struct GsrMonitor {
+ std::string name;
+ mgl::vec2i size;
+ };
+
+ struct SupportedCaptureOptions {
+ bool window = false;
+ bool focused = false;
+ bool screen = false;
+ bool portal = false;
+ std::vector<GsrMonitor> monitors;
+ };
+
+ enum class DisplayServer {
+ UNKNOWN,
+ X11,
+ WAYLAND
+ };
+
+ struct SystemInfo {
+ DisplayServer display_server = DisplayServer::UNKNOWN;
+ };
+
+ enum class GpuVendor {
+ UNKNOWN,
+ AMD,
+ INTEL,
+ NVIDIA
+ };
+
+ struct GpuInfo {
+ GpuVendor vendor = GpuVendor::UNKNOWN;
+ };
+
+ struct GsrInfo {
+ SystemInfo system_info;
+ GpuInfo gpu_info;
+ SupportedVideoCodecs supported_video_codecs;
+ SupportedCaptureOptions supported_capture_options;
+ };
+
+ enum class GsrInfoExitStatus {
+ OK,
+ FAILED_TO_RUN_COMMAND,
+ OPENGL_FAILED,
+ NO_DRM_CARD
+ };
+
+ GsrInfoExitStatus get_gpu_screen_recorder_info(GsrInfo *gsr_info);
+} \ No newline at end of file
diff --git a/include/Theme.hpp b/include/Theme.hpp
new file mode 100644
index 0000000..182d264
--- /dev/null
+++ b/include/Theme.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mglpp/graphics/Color.hpp>
+
+namespace gsr {
+ struct GsrInfo;
+
+ struct Theme {
+ Theme() = default;
+ Theme(const Theme&) = delete;
+ Theme& operator=(const Theme&) = delete;
+
+ mgl::Color tint_color = mgl::Color(118, 185, 0);
+ };
+
+ void init_theme(const gsr::GsrInfo &gsr_info);
+ const Theme& get_theme();
+} \ No newline at end of file
diff --git a/include/gui/DropdownButton.hpp b/include/gui/DropdownButton.hpp
new file mode 100644
index 0000000..bcf769c
--- /dev/null
+++ b/include/gui/DropdownButton.hpp
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "Widget.hpp"
+#include <string>
+#include <functional>
+#include <vector>
+#include <mglpp/graphics/Text.hpp>
+#include <mglpp/graphics/Sprite.hpp>
+
+namespace gsr {
+ class DropdownButton : public Widget {
+ public:
+ DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description_activated, const char *description_deactivated, mgl::Texture *icon_texture, mgl::vec2f size);
+ DropdownButton(const DropdownButton&) = delete;
+ DropdownButton& operator=(const DropdownButton&) = delete;
+
+ bool on_event(mgl::Event &event, mgl::Window &window) override;
+ void draw(mgl::Window &window) override;
+
+ void add_item(const std::string &text, const std::string &id);
+ void set_item_label(const std::string &id, const std::string &new_label);
+
+ void set_activated(bool activated);
+
+ mgl::vec2f get_size();
+
+ std::function<void(const std::string &id)> on_click;
+ private:
+ void update_if_dirty();
+ private:
+ struct Item {
+ mgl::Text text;
+ std::string id;
+ };
+
+ std::vector<Item> items;
+ mgl::Font *title_font;
+ mgl::Font *description_font;
+ mgl::vec2f size;
+ bool mouse_inside = false;
+ bool show_dropdown = false;
+ bool dirty = true;
+ mgl::vec2f max_size;
+ int mouse_inside_item = -1;
+
+ mgl::Text title;
+ mgl::Text description;
+ mgl::Sprite icon_sprite;
+
+ std::string description_activated;
+ std::string description_deactivated;
+ bool activated = false;
+ };
+} \ No newline at end of file
diff --git a/include/gui/Utils.hpp b/include/gui/Utils.hpp
new file mode 100644
index 0000000..fe5ee49
--- /dev/null
+++ b/include/gui/Utils.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <mglpp/system/vec.hpp>
+#include <mglpp/graphics/Color.hpp>
+
+namespace mgl {
+ class Window;
+}
+
+namespace gsr {
+ // Inner border
+ void draw_rectangle_outline(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::Color color, float border_size);
+} \ No newline at end of file
diff --git a/src/GsrInfo.cpp b/src/GsrInfo.cpp
new file mode 100644
index 0000000..1534363
--- /dev/null
+++ b/src/GsrInfo.cpp
@@ -0,0 +1,179 @@
+#include "../include/GsrInfo.hpp"
+#include <string.h>
+#include <functional>
+
+namespace gsr {
+ using StringSplitCallback = std::function<bool(std::string_view line)>;
+
+ static void string_split_char(const std::string &str, char delimiter, StringSplitCallback callback_func) {
+ size_t index = 0;
+ while(index < str.size()) {
+ size_t new_index = str.find(delimiter, index);
+ if(new_index == std::string::npos)
+ new_index = str.size();
+
+ if(!callback_func({str.data() + index, new_index - index}))
+ break;
+
+ index = new_index + 1;
+ }
+ }
+
+ static void parse_system_info_line(GsrInfo *gsr_info, const std::string &line) {
+ const size_t space_index = line.find(' ');
+ if(space_index == std::string::npos)
+ return;
+
+ const std::string_view attribute_name = {line.c_str(), space_index};
+ const std::string_view attribute_value = {line.c_str() + space_index + 1, line.size() - (space_index + 1)};
+ if(attribute_name == "display_server") {
+ if(attribute_value == "x11")
+ gsr_info->system_info.display_server = DisplayServer::X11;
+ else if(attribute_value == "wayland")
+ gsr_info->system_info.display_server = DisplayServer::WAYLAND;
+ }
+ }
+
+ static void parse_gpu_info_line(GsrInfo *gsr_info, const std::string &line) {
+ const size_t space_index = line.find(' ');
+ if(space_index == std::string::npos)
+ return;
+
+ const std::string_view attribute_name = {line.c_str(), space_index};
+ const std::string_view attribute_value = {line.c_str() + space_index + 1, line.size() - (space_index + 1)};
+ if(attribute_name == "vendor") {
+ if(attribute_value == "amd")
+ gsr_info->gpu_info.vendor = GpuVendor::AMD;
+ else if(attribute_value == "intel")
+ gsr_info->gpu_info.vendor = GpuVendor::INTEL;
+ else if(attribute_value == "nvidia")
+ gsr_info->gpu_info.vendor = GpuVendor::NVIDIA;
+ }
+ }
+
+ static void parse_video_codecs_line(GsrInfo *gsr_info, const std::string &line) {
+ if(line == "h264")
+ gsr_info->supported_video_codecs.h264 = true;
+ else if(line == "hevc")
+ gsr_info->supported_video_codecs.hevc = true;
+ else if(line == "av1")
+ gsr_info->supported_video_codecs.av1 = true;
+ else if(line == "vp8")
+ gsr_info->supported_video_codecs.vp8 = true;
+ else if(line == "vp9")
+ gsr_info->supported_video_codecs.vp9 = true;
+ }
+
+ static GsrMonitor capture_option_line_to_monitor(const std::string &line) {
+ size_t space_index = line.find(' ');
+ if(space_index == std::string::npos)
+ return { line, {0, 0} };
+
+ mgl::vec2i size = {0, 0};
+ if(sscanf(line.c_str() + space_index + 1, "%dx%d", &size.x, &size.y) != 2)
+ size = {0, 0};
+
+ return { line.substr(0, space_index), size };
+ }
+
+ static void parse_capture_options_line(GsrInfo *gsr_info, const std::string &line) {
+ if(line == "window")
+ gsr_info->supported_capture_options.window = true;
+ else if(line == "focused")
+ gsr_info->supported_capture_options.focused = true;
+ else if(line == "screen")
+ gsr_info->supported_capture_options.screen = true;
+ else if(line == "portal")
+ gsr_info->supported_capture_options.portal = true;
+ else
+ gsr_info->supported_capture_options.monitors.push_back(capture_option_line_to_monitor(line));
+ }
+
+ enum class GsrInfoSection {
+ UNKNOWN,
+ SYSTEM_INFO,
+ GPU_INFO,
+ VIDEO_CODECS,
+ CAPTURE_OPTIONS
+ };
+
+ static bool starts_with(const std::string &str, const char *substr) {
+ size_t len = strlen(substr);
+ return str.size() >= len && memcmp(str.data(), substr, len) == 0;
+ }
+
+ GsrInfoExitStatus get_gpu_screen_recorder_info(GsrInfo *gsr_info) {
+ *gsr_info = GsrInfo{};
+
+ FILE *f = popen("gpu-screen-recorder --info", "r");
+ if(!f) {
+ fprintf(stderr, "error: 'gpu-screen-recorder --info' failed\n");
+ return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
+ }
+
+ char output[8192];
+ ssize_t bytes_read = fread(output, 1, sizeof(output) - 1, f);
+ if(bytes_read < 0 || ferror(f)) {
+ fprintf(stderr, "error: failed to read 'gpu-screen-recorder --info' output\n");
+ pclose(f);
+ return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
+ }
+ output[bytes_read] = '\0';
+
+ GsrInfoSection section = GsrInfoSection::UNKNOWN;
+ string_split_char(output, '\n', [&](std::string_view line) {
+ const std::string line_str(line.data(), line.size());
+
+ if(starts_with(line_str, "section=")) {
+ const char *section_name = line_str.c_str() + 8;
+ if(strcmp(section_name, "system_info") == 0)
+ section = GsrInfoSection::SYSTEM_INFO;
+ else if(strcmp(section_name, "gpu_info") == 0)
+ section = GsrInfoSection::GPU_INFO;
+ else if(strcmp(section_name, "video_codecs") == 0)
+ section = GsrInfoSection::VIDEO_CODECS;
+ else if(strcmp(section_name, "capture_options") == 0)
+ section = GsrInfoSection::CAPTURE_OPTIONS;
+ else
+ section = GsrInfoSection::UNKNOWN;
+ return true;
+ }
+
+ switch(section) {
+ case GsrInfoSection::UNKNOWN: {
+ break;
+ }
+ case GsrInfoSection::SYSTEM_INFO: {
+ parse_system_info_line(gsr_info, line_str);
+ break;
+ }
+ case GsrInfoSection::GPU_INFO: {
+ parse_gpu_info_line(gsr_info, line_str);
+ break;
+ }
+ case GsrInfoSection::VIDEO_CODECS: {
+ parse_video_codecs_line(gsr_info, line_str);
+ break;
+ }
+ case GsrInfoSection::CAPTURE_OPTIONS: {
+ parse_capture_options_line(gsr_info, line_str);
+ break;
+ }
+ }
+
+ return true;
+ });
+
+ int status = pclose(f);
+ if(WIFEXITED(status)) {
+ switch(WEXITSTATUS(status)) {
+ case 0: return GsrInfoExitStatus::OK;
+ case 22: return GsrInfoExitStatus::OPENGL_FAILED;
+ case 23: return GsrInfoExitStatus::NO_DRM_CARD;
+ default: return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
+ }
+ }
+
+ return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
+ }
+} \ No newline at end of file
diff --git a/src/Theme.cpp b/src/Theme.cpp
new file mode 100644
index 0000000..0f44126
--- /dev/null
+++ b/src/Theme.cpp
@@ -0,0 +1,34 @@
+#include "../include/Theme.hpp"
+#include "../include/GsrInfo.hpp"
+#include <assert.h>
+
+namespace gsr {
+ static Theme theme;
+ static bool initialized = false;
+
+ void init_theme(const gsr::GsrInfo &gsr_info) {
+ switch(gsr_info.gpu_info.vendor) {
+ case gsr::GpuVendor::UNKNOWN: {
+ break;
+ }
+ case gsr::GpuVendor::AMD: {
+ theme.tint_color = mgl::Color(221, 0, 49);
+ break;
+ }
+ case gsr::GpuVendor::INTEL: {
+ theme.tint_color = mgl::Color(8, 109, 183);
+ break;
+ }
+ case gsr::GpuVendor::NVIDIA: {
+ theme.tint_color = mgl::Color(118, 185, 0);
+ break;
+ }
+ }
+ initialized = true;
+ }
+
+ const Theme& get_theme() {
+ assert(initialized);
+ return theme;
+ }
+} \ No newline at end of file
diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp
index b9d6d7c..3cad31f 100644
--- a/src/gui/Button.cpp
+++ b/src/gui/Button.cpp
@@ -1,4 +1,5 @@
#include "../../include/gui/Button.hpp"
+#include "../../include/Theme.hpp"
#include <mglpp/graphics/Rectangle.hpp>
#include <mglpp/window/Window.hpp>
#include <mglpp/window/Event.hpp>
@@ -40,7 +41,7 @@ namespace gsr {
}
const int border_size = 5;
- const mgl::Color border_color(118, 185, 0);
+ const mgl::Color border_color = gsr::get_theme().tint_color;
// Green line at top
{
diff --git a/src/gui/ComboBox.cpp b/src/gui/ComboBox.cpp
index 00e8bc3..dd683e1 100644
--- a/src/gui/ComboBox.cpp
+++ b/src/gui/ComboBox.cpp
@@ -1,4 +1,6 @@
#include "../../include/gui/ComboBox.hpp"
+#include "../../include/gui/Utils.hpp"
+#include "../../include/Theme.hpp"
#include <mglpp/graphics/Rectangle.hpp>
#include <mglpp/graphics/Font.hpp>
#include <mglpp/window/Window.hpp>
@@ -46,40 +48,6 @@ namespace gsr {
return true;
}
- static void draw_rectangle_outline(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::Color color, float border_size) {
- // Green line at top
- {
- mgl::Rectangle rect({ size.x, border_size });
- rect.set_position(pos);
- rect.set_color(color);
- window.draw(rect);
- }
-
- // Green line at bottom
- {
- mgl::Rectangle rect({ size.x, border_size });
- rect.set_position(pos + mgl::vec2f(0.0f, size.y - border_size));
- rect.set_color(color);
- window.draw(rect);
- }
-
- // Green line at left
- {
- mgl::Rectangle rect({ border_size, size.y - border_size * 2 });
- rect.set_position(pos + mgl::vec2f(0, border_size));
- rect.set_color(color);
- window.draw(rect);
- }
-
- // Green line at right
- {
- mgl::Rectangle rect({ border_size, size.y - border_size * 2 });
- rect.set_position(pos + mgl::vec2f(size.x - border_size, border_size));
- rect.set_color(color);
- window.draw(rect);
- }
- }
-
void ComboBox::draw(mgl::Window &window) {
update_if_dirty();
@@ -102,10 +70,10 @@ namespace gsr {
mgl::vec2f pos = position + mgl::vec2f(padding_left, padding_top);
Item &item = items[selected_item];
- item.text.set_position(pos);
+ item.text.set_position(pos.floor());
if(show_dropdown) {
const int border_size = 3;
- const mgl::Color border_color(118, 185, 0);
+ const mgl::Color border_color = gsr::get_theme().tint_color;
draw_rectangle_outline(window, pos - mgl::vec2f(padding_left, padding_top), item_size, border_color, border_size);
}
window.draw(item.text);
@@ -113,7 +81,7 @@ namespace gsr {
for(size_t i = 0; i < items.size(); ++i) {
Item &item = items[i];
- item.text.set_position(pos);
+ item.text.set_position(pos.floor());
const mgl::FloatRect text_bounds = item.text.get_bounds();
if(show_dropdown) {
@@ -121,7 +89,7 @@ namespace gsr {
inside = mgl::FloatRect(text_bounds.position - mgl::vec2f(padding_left, padding_top), item_size).contains({ (float)mouse_pos.x, (float)mouse_pos.y });
if(inside) {
mgl::Rectangle item_background(text_bounds.position - mgl::vec2f(padding_left, padding_top), item_size);
- item_background.set_color(mgl::Color(118, 185, 0));
+ item_background.set_color(gsr::get_theme().tint_color);
window.draw(item_background);
} else {
/*const int border_size = 3;
diff --git a/src/gui/DropdownButton.cpp b/src/gui/DropdownButton.cpp
new file mode 100644
index 0000000..9610e26
--- /dev/null
+++ b/src/gui/DropdownButton.cpp
@@ -0,0 +1,181 @@
+#include "../../include/gui/DropdownButton.hpp"
+#include "../../include/gui/Utils.hpp"
+#include "../../include/Theme.hpp"
+#include <mglpp/graphics/Rectangle.hpp>
+#include <mglpp/graphics/Texture.hpp>
+#include <mglpp/window/Window.hpp>
+#include <mglpp/window/Event.hpp>
+#include <mglpp/system/FloatRect.hpp>
+
+namespace gsr {
+ static const float padding_top = 10.0f;
+ static const float padding_bottom = 10.0f;
+ static const float padding_left = 10.0f;
+ static const float padding_right = 10.0f;
+ static const int border_size = 5;
+
+ DropdownButton::DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description_activated, const char *description_deactivated, mgl::Texture *icon_texture, mgl::vec2f size) :
+ title_font(title_font), description_font(description_font), size(size), title(title, *title_font), description(description_deactivated, *description_font),
+ description_activated(description_activated), description_deactivated(description_deactivated)
+ {
+ if(icon_texture && icon_texture->is_valid()) {
+ icon_sprite.set_texture(icon_texture);
+ icon_sprite.set_height((int)(size.y * 0.5f));
+ }
+ this->description.set_color(mgl::Color(150, 150, 150));
+ }
+
+ bool DropdownButton::on_event(mgl::Event &event, mgl::Window&) {
+ if(event.type == mgl::Event::MouseMoved) {
+ const mgl::vec2f collision_margin(1.0f, 1.0f); // Makes sure that multiple buttons that are next to each other wont activate at the same time when the cursor is right between them
+ const bool inside = mgl::FloatRect(position + collision_margin, size - collision_margin).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;
+ }
+ } else if(event.type == mgl::Event::MouseButtonPressed) {
+ const bool clicked_inside = mouse_inside;
+ show_dropdown = clicked_inside;
+ if(on_click && mouse_inside_item >= 0 && mouse_inside_item < (int)items.size())
+ on_click(items[mouse_inside_item].id);
+ }
+ return true;
+ }
+
+ void DropdownButton::draw(mgl::Window &window) {
+ update_if_dirty();
+
+ if(show_dropdown) {
+ // Background
+ {
+ mgl::Rectangle rect(size);
+ rect.set_position(position);
+ rect.set_color(mgl::Color(0, 0, 0, 255));
+ window.draw(rect);
+ }
+
+ const mgl::Color border_color = gsr::get_theme().tint_color;
+
+ // Green line at top
+ {
+ mgl::Rectangle rect({ size.x, border_size });
+ rect.set_position(position);
+ rect.set_color(border_color);
+ window.draw(rect);
+ }
+ } else if(mouse_inside) {
+ // Background
+ {
+ mgl::Rectangle rect(size);
+ rect.set_position(position);
+ rect.set_color(mgl::Color(0, 0, 0, 255));
+ window.draw(rect);
+ }
+
+ const mgl::Color border_color = gsr::get_theme().tint_color;
+ draw_rectangle_outline(window, position, size, border_color, border_size);
+ } else {
+ // Background
+ mgl::Rectangle rect(size);
+ rect.set_position(position);
+ rect.set_color(mgl::Color(0, 0, 0, 220));
+ window.draw(rect);
+ }
+
+ const int text_margin = size.y * 0.085;
+
+ const auto title_bounds = title.get_bounds();
+ title.set_position((position + mgl::vec2f(size.x * 0.5f - title_bounds.size.x * 0.5f, text_margin)).floor());
+ window.draw(title);
+
+ const auto description_bounds = description.get_bounds();
+ description.set_position((position + mgl::vec2f(size.x * 0.5f - description_bounds.size.x * 0.5f, size.y - description_bounds.size.y - text_margin)).floor());
+ window.draw(description);
+
+ if(icon_sprite.get_texture()->is_valid()) {
+ icon_sprite.set_position((position + size * 0.5f - icon_sprite.get_size() * 0.5f).floor());
+ window.draw(icon_sprite);
+ }
+
+ mouse_inside_item = -1;
+ if(show_dropdown) {
+ const mgl::vec2i mouse_pos = window.get_mouse_position();
+
+ mgl::Rectangle dropdown_bg(max_size);
+ dropdown_bg.set_position(position + mgl::vec2f(0.0f, size.y));
+ dropdown_bg.set_color(mgl::Color(0, 0, 0));
+ window.draw(dropdown_bg);
+
+ mgl::vec2f item_position = dropdown_bg.get_position();
+ for(size_t i = 0; i < items.size(); ++i) {
+ auto &item = items[i];
+ const auto text_bounds = item.text.get_bounds();
+ const float item_height = padding_top + text_bounds.size.y + padding_bottom;
+
+ if(mouse_inside_item == -1) {
+ const mgl::vec2f item_size(max_size.x, item_height);
+ const bool inside = mgl::FloatRect(item_position, item_size).contains({ (float)mouse_pos.x, (float)mouse_pos.y });
+ if(inside) {
+ draw_rectangle_outline(window, item_position, item_size, gsr::get_theme().tint_color, 5);
+ mouse_inside_item = i;
+ }
+ }
+
+ item.text.set_position((item_position + mgl::vec2f(padding_left, item_height * 0.5f - text_bounds.size.y * 0.5f)).floor());
+ window.draw(item.text);
+ item_position.y += item_height;
+ }
+ }
+ }
+
+ void DropdownButton::add_item(const std::string &text, const std::string &id) {
+ items.push_back({mgl::Text(text, *title_font), id});
+ dirty = true;
+ }
+
+ void DropdownButton::set_item_label(const std::string &id, const std::string &new_label) {
+ for(auto &item : items) {
+ if(item.id == id) {
+ item.text.set_string(new_label);
+ dirty = true;
+ return;
+ }
+ }
+ }
+
+ void DropdownButton::set_activated(bool activated) {
+ if(this->activated == activated)
+ return;
+
+ this->activated = activated;
+
+ if(activated) {
+ description = mgl::Text(description_activated, *description_font);
+ description.set_color(get_theme().tint_color);
+ icon_sprite.set_color(get_theme().tint_color);
+ } else {
+ description = mgl::Text(description_deactivated, *description_font);
+ description.set_color(mgl::Color(150, 150, 150));
+ icon_sprite.set_color(mgl::Color(255, 255, 255));
+ }
+ }
+
+ void DropdownButton::update_if_dirty() {
+ if(!dirty)
+ return;
+
+ max_size = { size.x, 0.0f };
+ for(Item &item : items) {
+ const mgl::vec2f bounds = item.text.get_bounds().size;
+ max_size.x = std::max(max_size.x, bounds.x + padding_left + padding_right);
+ max_size.y += bounds.y + padding_top + padding_bottom;
+ }
+ dirty = false;
+ }
+
+ mgl::vec2f DropdownButton::get_size() {
+ update_if_dirty();
+ return size;
+ }
+} \ No newline at end of file
diff --git a/src/gui/Utils.cpp b/src/gui/Utils.cpp
new file mode 100644
index 0000000..20c7b73
--- /dev/null
+++ b/src/gui/Utils.cpp
@@ -0,0 +1,43 @@
+#include "../../include/gui/Utils.hpp"
+#include <mglpp/window/Window.hpp>
+#include <mglpp/graphics/Rectangle.hpp>
+
+namespace gsr {
+ // TODO: Use vertices to make it one draw call
+ void draw_rectangle_outline(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::Color color, float border_size) {
+ pos = pos.floor();
+ size = size.floor();
+ border_size = (int)border_size;
+ // Green line at top
+ {
+ mgl::Rectangle rect({ size.x, border_size });
+ rect.set_position(pos);
+ rect.set_color(color);
+ window.draw(rect);
+ }
+
+ // Green line at bottom
+ {
+ mgl::Rectangle rect({ size.x, border_size });
+ rect.set_position(pos + mgl::vec2f(0.0f, size.y - border_size));
+ rect.set_color(color);
+ window.draw(rect);
+ }
+
+ // Green line at left
+ {
+ mgl::Rectangle rect({ border_size, size.y - border_size * 2 });
+ rect.set_position(pos + mgl::vec2f(0, border_size));
+ rect.set_color(color);
+ window.draw(rect);
+ }
+
+ // Green line at right
+ {
+ mgl::Rectangle rect({ border_size, size.y - border_size * 2 });
+ rect.set_position(pos + mgl::vec2f(size.x - border_size, border_size));
+ rect.set_color(color);
+ window.draw(rect);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/gui/WidgetContainer.cpp b/src/gui/WidgetContainer.cpp
index ab97b5b..8824d1a 100644
--- a/src/gui/WidgetContainer.cpp
+++ b/src/gui/WidgetContainer.cpp
@@ -42,10 +42,11 @@ namespace gsr {
Widget *widget = *it;
if(widget->move_to_top) {
widget->move_to_top = false;
- if(widgets.back() != widget) {
+ std::swap(*it, widgets.back());
+ /*if(widgets.back() != widget) {
widgets.erase(it);
widgets.push_back(widget);
- }
+ }*/
}
}
diff --git a/src/main.cpp b/src/main.cpp
index 0fb7b95..0ecd715 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,8 +1,10 @@
#include "../include/gui/WidgetContainer.hpp"
-#include "../include/gui/Button.hpp"
+#include "../include/gui/DropdownButton.hpp"
#include "../include/gui/ComboBox.hpp"
#include "../include/Process.hpp"
+#include "../include/Theme.hpp"
+#include "../include/GsrInfo.hpp"
#include <stdio.h>
#include <stdlib.h>
@@ -138,6 +140,15 @@ int main(int argc, char **argv) {
signal(SIGINT, sigint_handler);
+ gsr::GsrInfo gsr_info;
+ // TODO:
+ gsr::GsrInfoExitStatus gsr_info_exit_status = gsr::get_gpu_screen_recorder_info(&gsr_info);
+ if(gsr_info_exit_status != gsr::GsrInfoExitStatus::OK) {
+ fprintf(stderr, "error: failed to get gpu-screen-recorder info\n");
+ exit(1);
+ }
+ gsr::init_theme(gsr_info);
+
std::string program_root_dir = dirname(argv[0]);
if(!program_root_dir.empty() && program_root_dir.back() != '/')
program_root_dir += '/';
@@ -225,10 +236,7 @@ int main(int argc, char **argv) {
bg_screenshot_overlay.set_color(bg_color);
struct MainButton {
- mgl::Text title;
- mgl::Text description;
- mgl::Sprite icon;
- std::unique_ptr<gsr::Button> button;
+ std::unique_ptr<gsr::DropdownButton> button;
gsr::GsrMode mode;
};
@@ -256,26 +264,17 @@ int main(int argc, char **argv) {
&stream_button_texture
};
- const int button_height = window_create_params.size.y / 6.0f;
+ const int button_height = window_create_params.size.y / 5.0f;
const int button_width = button_height;
std::vector<MainButton> main_buttons;
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_off[i], {0.0f, 0.0f}, font);
- description.set_color(mgl::Color(150, 150, 150));
-
- mgl::Sprite sprite(textures[i]);
- sprite.set_height(button_height * 0.5f);
- auto button = std::make_unique<gsr::Button>(mgl::vec2f(button_width, button_height));
+ auto button = std::make_unique<gsr::DropdownButton>(&title_font, &font, titles[i], descriptions_on[i], descriptions_off[i], textures[i], mgl::vec2f(button_width, button_height));
+ button->add_item("Start", "start");
+ button->add_item("Settings", "settings");
MainButton main_button = {
- std::move(title),
- std::move(description),
- std::move(sprite),
std::move(button),
gsr::GsrMode::Unknown
};
@@ -285,7 +284,6 @@ int main(int argc, char **argv) {
auto update_overlay_shape = [&](std::optional<gsr::GsrMode> gsr_mode = std::nullopt) {
fprintf(stderr, "update overlay shape!\n");
- const int main_button_margin = button_height * 0.085;// * get_config().scale;
const int spacing = 0;// * get_config().scale;
const int combined_spacing = spacing * std::max(0, (int)main_buttons.size() - 1);
@@ -303,37 +301,18 @@ int main(int argc, char **argv) {
for(size_t i = 0; i < main_buttons.size(); ++i) {
if(main_buttons[i].mode != gsr::GsrMode::Unknown && main_buttons[i].mode == gsr_mode.value()) {
- main_buttons[i].description.set_string(descriptions_on[i]);
- main_buttons[i].description.set_color(mgl::Color(118, 185, 0));
- main_buttons[i].icon.set_color(mgl::Color(118, 185, 0));
+ main_buttons[i].button->set_activated(true);
} else {
- main_buttons[i].description.set_string(descriptions_off[i]);
- main_buttons[i].description.set_color(mgl::Color(150, 150, 150));
- main_buttons[i].icon.set_color(mgl::Color(255, 255, 255));
+ main_buttons[i].button->set_activated(false);
}
- 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 * main_buttons[i].icon.get_scale().x * 0.5f,
- main_button_pos.y + overlay_desired_size.y * 0.5f - main_buttons[i].icon.get_texture()->get_size().y * main_buttons[i].icon.get_scale().y * 0.5f).floor());
-
main_buttons[i].button->set_position(main_button_pos.to_vec2f());
main_button_pos.x += per_button_width + combined_spacing;
}
};
// Replay
- main_buttons[0].button->on_click = [&]() {
+ main_buttons[0].button->on_click = [&](const std::string &id) {
/*
char window_to_record_str[32];
snprintf(window_to_record_str, sizeof(window_to_record_str), "%ld", target_window);
@@ -353,9 +332,12 @@ int main(int argc, char **argv) {
// TODO: Monitor /tmp/gpu-screen-recorder and update ui to match state
// Record
- main_buttons[1].button->on_click = [&]() {
- window.close();
- usleep(1000 * 50); // 50 milliseconds
+ main_buttons[1].button->on_click = [&](const std::string &id) {
+ if(id != "start")
+ return;
+
+ // window.close();
+ // usleep(1000 * 50); // 50 milliseconds
pid_t gpu_screen_recorder_process = -1;
gsr::GsrMode gsr_mode = gsr::GsrMode::Unknown;
@@ -366,12 +348,13 @@ int main(int argc, char **argv) {
perror("waitpid failed");
/* Ignore... */
}
- window.set_visible(false);
- window.close();
- return;
+ // window.set_visible(false);
+ // window.close();
+ // return;
//exit(0);
- //update_overlay_shape(gsr::GsrMode::Unknown);
- //return;
+ update_overlay_shape(gsr::GsrMode::Unknown);
+ main_buttons[1].button->set_item_label(id, "Start");
+ return;
}
const char *args[] = {
@@ -382,10 +365,14 @@ int main(int argc, char **argv) {
nullptr
};
gsr::exec_program_daemonized(args);
- //update_overlay_shape(gsr::GsrMode::Record);
+ update_overlay_shape(gsr::GsrMode::Record);
+ main_buttons[1].button->set_item_label(id, "Stop");
//exit(0);
- window.set_visible(false);
- window.close();
+ // window.set_visible(false);
+ // window.close();
+
+ // TODO: Show notification with args:
+ // "Recording has started" 3.0 ./images/record.png 76b900
};
main_buttons[1].mode = gsr::GsrMode::Record;
@@ -415,11 +402,11 @@ int main(int argc, char **argv) {
//XGrabServer(display);
- mgl::Rectangle top_bar_background(mgl::vec2f(window.get_size().x, window.get_size().y*0.05f).floor());
+ mgl::Rectangle top_bar_background(mgl::vec2f(window.get_size().x, window.get_size().y*0.06f).floor());
top_bar_background.set_color(mgl::Color(0, 0, 0, 220));
mgl::Text top_bar_text("GPU Screen Recorder", top_bar_font);
- //top_bar_text.set_color(mgl::Color(118, 185, 0));
+ //top_bar_text.set_color(gsr::get_theme().tint_color);
top_bar_text.set_position((top_bar_background.get_position() + top_bar_background.get_size()*0.5f - top_bar_text.get_bounds().size*0.5f).floor());
// gsr::ComboBox record_area_box(&title_font);
@@ -458,7 +445,7 @@ int main(int argc, char **argv) {
// framerate_title.set_position(mgl::vec2f(framerate_box.get_position().x, framerate_box.get_position().y - title_font.get_character_size() - 10.0f));
mgl::Texture close_texture;
- if(!close_texture.load_from_file("images/cross.png", {false, false, false}))
+ if(!close_texture.load_from_file("images/cross.png"))
startup_error("failed to load texture: images/cross.png");
mgl::Sprite close_sprite(&close_texture);
@@ -489,7 +476,7 @@ int main(int argc, char **argv) {
const float settings_topline_thickness = 5.0f;
mgl::Rectangle settings_topline(settings_background.get_position() - mgl::vec2f(0.0f, settings_topline_thickness), mgl::vec2f(settings_background.get_size().x, settings_topline_thickness));
- settings_topline.set_color(mgl::Color(118, 185, 0));
+ settings_topline.set_color(gsr::get_theme().tint_color);
*/
mgl::Clock state_update_timer;
@@ -517,11 +504,6 @@ int main(int argc, char **argv) {
// window.draw(video_quality_title);
// window.draw(framerate_title);
widget_container.draw(window);
- for(auto &main_button : main_buttons) {
- window.draw(main_button.icon);
- window.draw(main_button.title);
- window.draw(main_button.description);
- }
window.draw(top_bar_background);
window.draw(top_bar_text);
window.draw(logo_sprite);