From 8e6cbea1388c65f9f4bd448531ca8c9374b3f00a Mon Sep 17 00:00:00 2001 From: Prince Roshan Date: Tue, 8 Jul 2025 22:12:57 +0530 Subject: [PATCH 1/4] Add basic gui tray app --- README.md | 21 ++++++++++++ build_deb.sh | 70 ++++++++++++++++++++++++++++++++++++-- include/clipboard_mgmt.hpp | 1 + src/clipboard_mgmt.cpp | 40 ++++++++++++++++++++++ 4 files changed, 129 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 332e6bf..3ba15ec 100644 --- a/README.md +++ b/README.md @@ -130,3 +130,24 @@ hcp 2 ## License See [LICENSE](LICENSE). + +## Manual Page + +After installing the package, you can view the manual page for usage and options: + +``` +man hcp +``` + +This provides detailed information about available commands and usage examples. + +## Starting the GUI Tray App + +After installing, you can enable and start the tray app (which sits in your system tray and provides clipboard history): + +``` +sudo systemctl enable hcp-tray@$(whoami).service +sudo systemctl start hcp-tray@$(whoami).service +``` + +This will launch the tray app in the background for your user session. diff --git a/build_deb.sh b/build_deb.sh index dd83f75..47b3f50 100755 --- a/build_deb.sh +++ b/build_deb.sh @@ -24,7 +24,7 @@ Architecture: amd64 Maintainer: Prince Roshan Description: HCP - Historical Clipboard Manager A historical clipboard manager with LRU history and systemd integration. -Depends: libx11-6 +Depends: libx11-6, libgtk-3-0, libappindicator3-1 EOF # Create postinst script directly @@ -60,13 +60,73 @@ echo "" echo "To check if your environment is set up correctly, run:" echo " hcp --diagnostic" echo "" +echo "To enable and start the GUI tray app (clipboard history in system tray), run:" +echo " sudo systemctl enable hcp-tray@${USERNAME}.service" +echo " sudo systemctl start hcp-tray@${USERNAME}.service" POSTINST chmod 755 $PKGDIR/DEBIAN/postinst -# Build the binary +# Build the main binary make cp hcp $PKGDIR/usr/bin/hcp +# Build the GUI tray app +cd gui && make && cd .. +cp gui/hcp-tray $PKGDIR/usr/bin/hcp-tray + +# Add systemd service for tray app +cat > $PKGDIR/etc/systemd/system/hcp-tray@.service < $PKGDIR/usr/share/man/man1/hcp.1 <<'EOF' +.TH hcp 1 "$(date +%Y-%m-%d)" "$VERSION" "hcp manual" +.SH NAME +hcp \- Historical Clipboard Manager for X11 +.SH SYNOPSIS +.B hcp +[service start | list | | pop | --help | -h] +.SH DESCRIPTION +.B hcp +is a lightweight clipboard manager for X11 systems. It captures clipboard entries, maintains a history, and allows you to list, print, or remove entries. Designed for reliability and minimalism, it works directly with the X11 clipboard and is suitable for use as a background service or on-demand. +.SH COMMANDS +.TP +.B service start +Start clipboard monitoring service in the background. +.TP +.B list +List clipboard history. +.TP +.B +Print clipboard entry at . +.TP +.B pop +Remove most recent clipboard entry. +.TP +.B --help, -h +Show this help message. +.SH AUTHOR +Written by the hcp project contributors. +.SH SEE ALSO +clipboard(1) +EOF +gzip -f $PKGDIR/usr/share/man/man1/hcp.1 + # Build the .deb package dpkg-deb --build $PKGDIR @@ -77,4 +137,8 @@ echo "After install, enable and start the service with:" echo " sudo systemctl daemon-reload" echo " sudo systemctl enable hcp@${USERNAME}.service" echo " sudo systemctl start hcp@${USERNAME}.service" -echo "\nYou can override the version by running: VERSION=your_version ./build_deb.sh" \ No newline at end of file +echo "To enable and start the GUI tray app (clipboard history in system tray), run:" +echo " sudo systemctl enable hcp-tray@${USERNAME}.service" +echo " sudo systemctl start hcp-tray@${USERNAME}.service" +echo "" +echo "You can override the version by running: VERSION=your_version ./build_deb.sh" \ No newline at end of file diff --git a/include/clipboard_mgmt.hpp b/include/clipboard_mgmt.hpp index d4b5fe6..ce8af87 100644 --- a/include/clipboard_mgmt.hpp +++ b/include/clipboard_mgmt.hpp @@ -4,4 +4,5 @@ #include std::string get_clipboard(Display *display); +void set_clipboard(Display *display, const std::string &text); #endif // CLIPBOARD_MGMT_HPP \ No newline at end of file diff --git a/src/clipboard_mgmt.cpp b/src/clipboard_mgmt.cpp index bf98572..ba44a0a 100644 --- a/src/clipboard_mgmt.cpp +++ b/src/clipboard_mgmt.cpp @@ -196,4 +196,44 @@ std::string get_clipboard(Display *display) { } XDestroyWindow(display, window); return clipboard_content; +} + +void set_clipboard(Display *display, const std::string &text) { + Atom clipboard = XInternAtom(display, "CLIPBOARD", False); + Atom utf8 = XInternAtom(display, "UTF8_STRING", False); + Atom targets[] = {utf8, XA_STRING}; + + Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, + 1, 1, 0, 0, 0); + XSetSelectionOwner(display, clipboard, window, CurrentTime); + + XEvent event; + bool running = true; + while (running) { + XNextEvent(display, &event); + + if (event.type == SelectionRequest) { + XSelectionRequestEvent *req = &event.xselectionrequest; + XEvent respond; + memset(&respond, 0, sizeof(respond)); + respond.xselection.type = SelectionNotify; + respond.xselection.display = req->display; + respond.xselection.requestor = req->requestor; + respond.xselection.selection = req->selection; + respond.xselection.target = req->target; + respond.xselection.time = req->time; + respond.xselection.property = None; + + if (req->target == utf8 || req->target == XA_STRING) { + XChangeProperty(display, req->requestor, req->property, req->target, 8, + PropModeReplace, (const unsigned char *)text.c_str(), + text.length()); + respond.xselection.property = req->property; + } + XSendEvent(display, req->requestor, 0, 0, &respond); + XFlush(display); + running = false; // Only serve one request + } + } + XDestroyWindow(display, window); } \ No newline at end of file From 027a59ea590a55897ed1db8ae2dfe87d18cb4939 Mon Sep 17 00:00:00 2001 From: Prince Roshan Date: Tue, 8 Jul 2025 22:17:29 +0530 Subject: [PATCH 2/4] fix: update test workflow --- .github/workflows/test.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3917f70..36b2e03 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,26 +22,23 @@ jobs: test: runs-on: ubuntu-latest steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: List workspace files + run: ls -lR - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libx11-dev libx11-6 dpkg xvfb xclip - - - name: Checkout code - uses: actions/checkout@v4 - + sudo apt-get install -y libx11-dev libx11-6 dpkg xvfb xclip libgtk-3-dev libappindicator3-dev - name: Build latest .deb run: | chmod +x build_deb.sh ./build_deb.sh - - name: Install hcp .deb run: | sudo dpkg -i hcp_*.deb sudo apt-get install -f -y - - name: Make test script executable run: chmod +x test_hcp.sh - - name: Run hcp integration test run: xvfb-run --auto-servernum --server-args='-screen 0 1024x768x24' ./test_hcp.sh \ No newline at end of file From 1e8fc37588024894bad411d57108fc9dc5b7324a Mon Sep 17 00:00:00 2001 From: Prince Roshan Date: Tue, 8 Jul 2025 22:23:04 +0530 Subject: [PATCH 3/4] feat: add gui app --- gui/Makefile | 14 ++++++++++ gui/README.md | 0 gui/trey_app.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 gui/Makefile create mode 100644 gui/README.md create mode 100644 gui/trey_app.cpp diff --git a/gui/Makefile b/gui/Makefile new file mode 100644 index 0000000..bb68174 --- /dev/null +++ b/gui/Makefile @@ -0,0 +1,14 @@ +CXX = g++ +CXXFLAGS = -std=c++11 `pkg-config --cflags gtk+-3.0 appindicator3-0.1` -I../include +LDFLAGS = `pkg-config --libs gtk+-3.0 appindicator3-0.1` -lX11 + +SRC = trey_app.cpp ../src/clipboard_mgmt.cpp ../src/db.cpp ../src/logging.cpp +TARGET = hcp-tray + +all: $(TARGET) + +$(TARGET): $(SRC) + $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) + +clean: + rm -f $(TARGET) \ No newline at end of file diff --git a/gui/README.md b/gui/README.md new file mode 100644 index 0000000..e69de29 diff --git a/gui/trey_app.cpp b/gui/trey_app.cpp new file mode 100644 index 0000000..a214b8d --- /dev/null +++ b/gui/trey_app.cpp @@ -0,0 +1,68 @@ +#include +#include +#include "../include/db.hpp" +#include "../include/clipboard_mgmt.hpp" +#include +#include +#include + +static void on_copy_clicked(GtkButton *button, gpointer user_data) { + const char *text = (const char *)user_data; + Display *display = XOpenDisplay(nullptr); + if (display) { + set_clipboard(display, std::string(text)); + XCloseDisplay(display); + } +} + +static void show_history_window(GtkWidget *, gpointer) { + GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), "Clipboard History"); + gtk_window_set_default_size(GTK_WINDOW(window), 400, 400); + + GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(window), scrolled); + + GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); + gtk_container_add(GTK_CONTAINER(scrolled), vbox); + + std::vector history = load_clipboard_blocks(); + for (const auto &entry : history) { + GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); + GtkWidget *label = gtk_label_new(entry.substr(0, 60).c_str()); + gtk_label_set_xalign(GTK_LABEL(label), 0.0); + GtkWidget *button = gtk_button_new_with_label("Copy"); + g_signal_connect(button, "clicked", G_CALLBACK(on_copy_clicked), (gpointer)entry.c_str()); + gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + } + gtk_widget_show_all(window); +} + +int main(int argc, char **argv) { + gtk_init(&argc, &argv); + + AppIndicator *indicator = app_indicator_new( + "clipboard-manager", + "system-run-symbolic", + APP_INDICATOR_CATEGORY_APPLICATION_STATUS + ); + app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE); + + GtkWidget *menu = gtk_menu_new(); + GtkWidget *item_show = gtk_menu_item_new_with_label("Show Clipboard History"); + g_signal_connect(item_show, "activate", G_CALLBACK(show_history_window), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item_show); + + GtkWidget *item_quit = gtk_menu_item_new_with_label("Quit"); + g_signal_connect(item_quit, "activate", G_CALLBACK(gtk_main_quit), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item_quit); + + gtk_widget_show_all(menu); + app_indicator_set_menu(indicator, GTK_MENU(menu)); + + gtk_main(); + return 0; +} + From 05dadc440fc5c873d5102acda5142e0f1edda746 Mon Sep 17 00:00:00 2001 From: Prince Roshan Date: Tue, 8 Jul 2025 22:29:10 +0530 Subject: [PATCH 4/4] Add dependies in build_deb.sh --- build_deb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_deb.sh b/build_deb.sh index 47b3f50..c80c891 100755 --- a/build_deb.sh +++ b/build_deb.sh @@ -12,7 +12,7 @@ mkdir -p $PKGDIR/usr/bin mkdir -p $PKGDIR/etc/systemd/system # Ensure build dependency is installed -apt-get update && apt-get install -y libx11-dev +apt-get update && apt-get install -y libx11-dev libgtk-3-dev libappindicator3-dev # Create control file cat > $PKGDIR/DEBIAN/control <