import os import subprocess from fabric.utils.helpers import exec_shell_command_async, get_relative_path from fabric.widgets.box import Box from fabric.widgets.button import Button from fabric.widgets.label import Label from gi.repository import Gdk, GLib from loguru import logger import config.data as data import modules.icons as icons SCREENSHOT_SCRIPT = get_relative_path("../scripts/screenshot.sh") POMODORO_SCRIPT = get_relative_path("../scripts/pomodoro.sh") OCR_SCRIPT = get_relative_path("../scripts/ocr.sh") GAMEMODE_SCRIPT = get_relative_path("../scripts/gamemode.sh") SCREENRECORD_SCRIPT = get_relative_path("../scripts/screenrecord.sh") # Tooltips ## Screenshot tooltip_ssregion = """Region Screenshot Left Click: Take a screenshot of a selected region. Right Click: Take a mockup screenshot of a selected region.""" tooltip_ssfull = """Screenshot Left Click: Take a fullscreen screenshot. Right Click: Take a mockup fullscreen screenshot.""" tooltip_sswindow = """Window Screenshot Left Click: Take a screenshot of the active window. Right Click: Take a mockup screenshot of the active window.""" tooltip_screenshots = "Screenshots Directory" tooltip_screenrecord = "Screen Recorder" tooltip_recordings = "Recordings Directory" tooltip_ocr = "OCR" tooltip_colorpicker = """Color Picker Mouse: Left Click: HEX Middle Click: HSV Right Click: RGB Keyboard: Enter: HEX Shift+Enter: RGB Ctrl+Enter: HSV""" tooltip_gamemode = "Game Mode\nDisables effects and window animations for better performance." tooltip_pomodoro = "Pomodoro Timer" tooltip_emoji = "Emoji Picker" class Toolbox(Box): def __init__(self, **kwargs): orientation = "h" if data.PANEL_THEME == "Panel" and (data.BAR_POSITION in ["Left", "Right"] or data.PANEL_POSITION in ["Start", "End"]): orientation = "v" super().__init__( name="toolbox", orientation=orientation, spacing=4, v_align="center", h_align="center", visible=True, **kwargs, ) self.notch = kwargs["notch"] self.btn_ssregion = Button( name="toolbox-button", tooltip_markup=tooltip_ssregion, child=Label(name="button-label", markup=icons.ssregion), on_clicked=self.ssregion, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_ssregion.set_can_focus(True) self.btn_ssregion.connect("button-press-event", self.on_ssregion_click) self.btn_ssregion.connect("key-press-event", self.on_ssregion_key) self.btn_ssfull = Button( name="toolbox-button", tooltip_markup=tooltip_ssfull, child=Label(name="button-label", markup=icons.ssfull), on_clicked=self.ssfull, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_ssfull.set_can_focus(True) self.btn_ssfull.connect("button-press-event", self.on_ssfull_click) self.btn_ssfull.connect("key-press-event", self.on_ssfull_key) self.btn_sswindow = Button( name="toolbox-button", tooltip_markup=tooltip_sswindow, child=Label(name="button-label", markup=icons.sswindow), on_clicked=self.sswindow, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_sswindow.set_can_focus(True) self.btn_sswindow.connect("button-press-event", self.on_sswindow_click) self.btn_sswindow.connect("key-press-event", self.on_sswindow_key) self.btn_screenrecord = Button( name="toolbox-button", tooltip_markup=tooltip_screenrecord, child=Label(name="button-label", markup=icons.screenrecord), on_clicked=self.screenrecord, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_ocr = Button( name="toolbox-button", tooltip_markup=tooltip_ocr, child=Label(name="button-label", markup=icons.ocr), on_clicked=self.ocr, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_color = Button( name="toolbox-button", tooltip_markup=tooltip_colorpicker, child=Label( name="button-bar-label", markup=icons.colorpicker ), h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_gamemode = Button( name="toolbox-button", tooltip_markup=tooltip_gamemode, child=Label(name="button-label", markup=icons.gamemode), on_clicked=self.gamemode, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_pomodoro = Button( name="toolbox-button", tooltip_markup=tooltip_pomodoro, child=Label(name="button-label", markup=icons.timer_off), on_clicked=self.pomodoro, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_color.set_can_focus(True) self.btn_color.connect("button-press-event", self.colorpicker) self.btn_color.connect("key_press_event", self.colorpicker_key) self.btn_emoji = Button( name="toolbox-button", tooltip_markup=tooltip_emoji, child=Label(name="button-label", markup=icons.emoji), on_clicked=self.emoji, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_screenshots_folder = Button( name="toolbox-button", tooltip_markup=tooltip_screenshots, child=Label(name="button-label", markup=icons.screenshots), on_clicked=self.open_screenshots_folder, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.btn_recordings_folder = Button( name="toolbox-button", tooltip_markup=tooltip_recordings, child=Label(name="button-label", markup=icons.recordings), on_clicked=self.open_recordings_folder, h_expand=False, v_expand=False, h_align="center", v_align="center", ) self.buttons = [ self.btn_ssregion, self.btn_sswindow, self.btn_ssfull, self.btn_screenshots_folder, Box(name="tool-sep", h_expand=False, v_expand=False, h_align="center", v_align="center"), self.btn_screenrecord, self.btn_recordings_folder, Box(name="tool-sep", h_expand=False, v_expand=False, h_align="center", v_align="center"), self.btn_ocr, self.btn_color, Box(name="tool-sep", h_expand=False, v_expand=False, h_align="center", v_align="center"), self.btn_gamemode, self.btn_pomodoro, self.btn_emoji, ] for button in self.buttons: self.add(button) self.show_all() self.recorder_timer_id = GLib.timeout_add_seconds(2, self.update_screenrecord_state) self.gamemode_updater = GLib.timeout_add_seconds(2, self.gamemode_check) self.pomodoro_updater = GLib.timeout_add_seconds(2, self.pomodoro_check) def close_menu(self): self.notch.close_notch() def ssfull(self, *args, mockup=False): cmd = f"bash {SCREENSHOT_SCRIPT} p" if mockup: cmd += " mockup" exec_shell_command_async(cmd) self.close_menu() def on_ssfull_click(self, button, event): if event.type == Gdk.EventType.BUTTON_PRESS: if event.button == 1: self.ssfull() elif event.button == 3: self.ssfull(mockup=True) return True return False def on_ssfull_key(self, widget, event): if event.keyval in {Gdk.KEY_Return, Gdk.KEY_KP_Enter}: modifiers = event.get_state() if modifiers & Gdk.ModifierType.SHIFT_MASK: self.ssfull(mockup=True) else: self.ssfull() return True return False def ssregion(self, *args): exec_shell_command_async(f"bash {SCREENSHOT_SCRIPT} s") self.close_menu() def on_ssregion_click(self, button, event): if event.type == Gdk.EventType.BUTTON_PRESS: if event.button == 1: self.ssregion() elif event.button == 3: exec_shell_command_async(f"bash {SCREENSHOT_SCRIPT} s mockup") self.close_menu() return True return False def on_ssregion_key(self, widget, event): if event.keyval in {Gdk.KEY_Return, Gdk.KEY_KP_Enter}: modifiers = event.get_state() if modifiers & Gdk.ModifierType.SHIFT_MASK: exec_shell_command_async(f"bash {SCREENSHOT_SCRIPT} s mockup") self.close_menu() else: self.ssregion() return True return False def sswindow(self, *args): exec_shell_command_async(f"bash {SCREENSHOT_SCRIPT} w") self.close_menu() def on_sswindow_click(self, button, event): if event.type == Gdk.EventType.BUTTON_PRESS: if event.button == 1: self.sswindow() elif event.button == 3: exec_shell_command_async(f"bash {SCREENSHOT_SCRIPT} w mockup") self.close_menu() return True return False def on_sswindow_key(self, widget, event): if event.keyval in {Gdk.KEY_Return, Gdk.KEY_KP_Enter}: modifiers = event.get_state() if modifiers & Gdk.ModifierType.SHIFT_MASK: exec_shell_command_async(f"bash {SCREENSHOT_SCRIPT} w mockup") self.close_menu() else: self.sswindow() return True return False def screenrecord(self, *args): exec_shell_command_async(f"bash -c 'nohup bash {SCREENRECORD_SCRIPT} > /dev/null 2>&1 & disown'") self.close_menu() def pomodoro(self, *args): exec_shell_command_async(f"bash -c 'nohup bash {POMODORO_SCRIPT} > /dev/null 2>&1 & disown'") self.close_menu() def pomodoro_check(self): """Check pomodoro status using proper background threading""" GLib.Thread.new("pomodoro-check", self._pomodoro_check_thread, None) return True def _pomodoro_check_thread(self, user_data): """Background thread to check pomodoro status""" try: result = subprocess.run("pgrep -f pomodoro.sh", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) running = result.returncode == 0 except Exception: running = False GLib.idle_add(self._update_pomodoro_ui, running) def _update_pomodoro_ui(self, running): """Update pomodoro UI from main thread""" if running: self.btn_pomodoro.get_child().set_markup(icons.timer_on) self.btn_pomodoro.add_style_class("pomodoro") else: self.btn_pomodoro.get_child().set_markup(icons.timer_off) self.btn_pomodoro.remove_style_class("pomodoro") return False def ocr(self, *args): exec_shell_command_async(f"bash {OCR_SCRIPT} s") self.close_menu() def gamemode(self, *args): exec_shell_command_async(f"bash {GAMEMODE_SCRIPT}") self.gamemode_check() self.close_menu() def gamemode_check(self): """Check gamemode status using proper background threading""" GLib.Thread.new("gamemode-check", self._gamemode_check_thread, None) return True def _gamemode_check_thread(self, user_data): """Background thread to check gamemode status""" try: result = subprocess.run(f"bash {GAMEMODE_SCRIPT} check", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) enabled = result.stdout == b't\n' except Exception: enabled = False GLib.idle_add(self._update_gamemode_ui, enabled) def _update_gamemode_ui(self, enabled): """Update gamemode UI from main thread""" if enabled: self.btn_gamemode.get_child().set_markup(icons.gamemode_off) else: self.btn_gamemode.get_child().set_markup(icons.gamemode) return False def colorpicker(self, button, event): if event.type == Gdk.EventType.BUTTON_PRESS: cmd = { 1: "-hex", 2: "-hsv", 3: "-rgb" }.get(event.button) if cmd: exec_shell_command_async(f"bash {get_relative_path('../scripts/hyprpicker.sh')} {cmd}") self.close_menu() def colorpicker_key(self, widget, event): if event.keyval in {Gdk.KEY_Return, Gdk.KEY_KP_Enter}: modifiers = event.get_state() cmd = "-hex" match modifiers & (Gdk.ModifierType.SHIFT_MASK | Gdk.ModifierType.CONTROL_MASK): case Gdk.ModifierType.SHIFT_MASK: cmd = "-rgb" case Gdk.ModifierType.CONTROL_MASK: cmd = "-hsv" exec_shell_command_async(f"bash {get_relative_path('../scripts/hyprpicker.sh')} {cmd}") self.close_menu() return True return False def update_screenrecord_state(self): """Check screen recording status using proper background threading""" GLib.Thread.new("screenrecord-check", self._screenrecord_check_thread, None) return True def _screenrecord_check_thread(self, user_data): """Background thread to check screen recording status""" try: result = subprocess.run("pgrep -f gpu-screen-recorder", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) running = result.returncode == 0 except Exception: running = False GLib.idle_add(self._update_screenrecord_ui, running) def _update_screenrecord_ui(self, running): """Update screen recording UI from main thread""" if running: self.btn_screenrecord.get_child().set_markup(icons.stop) self.btn_screenrecord.add_style_class("recording") else: self.btn_screenrecord.get_child().set_markup(icons.screenrecord) self.btn_screenrecord.remove_style_class("recording") return False def open_screenshots_folder(self, *args): screenshots_dir = os.path.join(os.environ.get('XDG_PICTURES_DIR', os.path.expanduser('~/Pictures')), 'Screenshots') os.makedirs(screenshots_dir, exist_ok=True) exec_shell_command_async(f"xdg-open {screenshots_dir}") self.close_menu() def open_recordings_folder(self, *args): recordings_dir = os.path.join(os.environ.get('XDG_VIDEOS_DIR', os.path.expanduser('~/Videos')), 'Recordings') os.makedirs(recordings_dir, exist_ok=True) exec_shell_command_async(f"xdg-open {recordings_dir}") self.close_menu() def emoji(self, *args): self.notch.open_notch("emoji")