diff options
author | Laura Aino Violetta Aléanor <lav@vampires.gay> | 2023-05-07 19:33:43 +0200 |
---|---|---|
committer | Laura Aino Violetta Aléanor <lav@vampires.gay> | 2023-05-07 19:33:43 +0200 |
commit | c9c7476a205bc7b30f6d7f218a1d794127f1bf2f (patch) | |
tree | 09d4b2978570f4b0e51a8a673598c8fc5d337452 | |
download | pickme-c9c7476a205bc7b30f6d7f218a1d794127f1bf2f.tar.gz pickme-c9c7476a205bc7b30f6d7f218a1d794127f1bf2f.zip |
Initial commit
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | config.h | 1 | ||||
-rw-r--r-- | pickme.c | 157 | ||||
-rw-r--r-- | unicode.hs | 37 |
4 files changed, 216 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6a0dc4b --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +HSSRCS := unicode.hs +CSRCS := pickme.c +OBJS := $(HSSRCS:.hs=.o) +TARGET := pickme +UNICODEDATA := UnicodeData.txt +CFLAGS := -I/usr/include/gtk-4.0 -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/lib64/libffi/include -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/lzo -I/usr/include/libpng16 -I/usr/include/pixman-1 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/graphene-1.0 -I/usr/lib64/graphene-1.0/include -optc-mfpmath=sse -optc-msse -optc-msse2 -optc-pthread -optc-g -optc-O #-optc-fsanitize=address -optl-fsanitize=address +LIBS := -optc-L/home/lav/.ghcup/ghc/9.6.1/lib/ghc-9.6.1/lib/x86_64-linux-ghc-9.6.1 -lgtk-4 -lpangocairo-1.0 -lpango-1.0 -lharfbuzz -lgdk_pixbuf-2.0 -lcairo-gobject -lcairo -lgraphene-1.0 -lgio-2.0 -lgobject-2.0 -lglib-2.0 + +all: $(TARGET) $(UNICODEDATA) + +clean: + @rm $(TARGET) $(OBJS) + +$(OBJS): $(HSSRCS) + ghc -c -O $^ + +$(TARGET): $(CSRCS) $(OBJS) + ghc --make -no-hs-main -g $(CFLAGS) $(LIBS) -o $(TARGET) $^ + +$(UNICODEDATA): + wget -O $@ -o /dev/null 'https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt' diff --git a/config.h b/config.h new file mode 100644 index 0000000..4016ef1 --- /dev/null +++ b/config.h @@ -0,0 +1 @@ +#define APP_ID "org.neocities.laleanor.pickme" diff --git a/pickme.c b/pickme.c new file mode 100644 index 0000000..f120a5a --- /dev/null +++ b/pickme.c @@ -0,0 +1,157 @@ +#include <HsFFI.h> +#ifdef __GLASGOW_HASKELL__ +//#include "unicode_stub.h" +#endif /* __GLASGOW_HASKELL__ */ + +#include <stdlib.h> +#include <gtk/gtk.h> +#include "config.h" + +GtkWidget *window; +GtkWidget *input; +GtkWidget *box; +GtkWidget *scwin; +GtkWidget *flowbox; +GtkEntryBuffer *input_buffer; +char *labels; +GtkWidget **buttons; + +bool inited = false; + +extern char *searchCharacterByName (const char *); + +void +on_click (GtkWidget *widget, + gpointer data) +{ + gdk_clipboard_set_text (gtk_widget_get_primary_clipboard (widget), (char *)data); +} + +void +on_input (GtkText *text, + gpointer user_data) +{ + if (!inited) + { + scwin = gtk_scrolled_window_new (); + flowbox = gtk_flow_box_new (); + gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scwin), flowbox); + gtk_box_append (GTK_BOX (box), scwin); + } + else + { + flowbox = gtk_flow_box_new (); + gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scwin), flowbox); + } + + inited = true; + + graphene_rect_t box_bounds; + if (gtk_widget_compute_bounds (box, window, &box_bounds)) + { + graphene_rect_t text_bounds; + if (gtk_widget_compute_bounds (GTK_WIDGET (text), window, &text_bounds)) + { + int height = box_bounds.size.height - text_bounds.size.height - 10; + gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (scwin), height); + gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (scwin), height); + } + } + + gsize buf_size = gtk_entry_buffer_get_bytes (input_buffer); + const char *buffer_contents = gtk_entry_buffer_get_text (input_buffer); + char *results; + + results = searchCharacterByName (buffer_contents); + size_t l = strlen (results); + + if (l == 0) + { + return; + } + + wchar_t symbols[l]; + int i = 0; + int o = 0; + int o_ = 0; + mbstowcs (symbols, results, l); + + if (buttons) + { + buttons = realloc (buttons, l * sizeof (GtkWidget *)); + } + else + { + buttons = malloc (l * sizeof (GtkWidget *)); + } + memset (buttons, 0, l * sizeof (GtkWidget *)); + + if (labels) + { + labels = realloc (labels, 5*l); + } + else + { + labels = malloc (5*l); + } + memset (labels, 0, 5*l); + + while (symbols[i]) + { + const wchar_t hold[] = { symbols[i], 0 }; + o_ += wcstombs (&labels[o], hold, 4) +1; + buttons[i] = gtk_button_new_with_label (&labels[o]); + g_signal_connect (buttons[i], "clicked", G_CALLBACK (on_click), &labels[o]); + gtk_flow_box_append ((GtkFlowBox *)flowbox, buttons[i]); + ++i; + o = o_; + } +} + +void +activate (GtkApplication *app, + gpointer user_data) +{ + window = gtk_application_window_new (app); + gtk_window_set_title (GTK_WINDOW (window), "pickme"); + gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); + + input_buffer = gtk_entry_buffer_new (NULL, -1); + input = gtk_text_new_with_buffer (input_buffer); + g_signal_connect (input, "activate", G_CALLBACK (on_input), NULL); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); + gtk_box_set_homogeneous (GTK_BOX (box), FALSE); + gtk_box_append (GTK_BOX (box), input); + gtk_window_set_child (GTK_WINDOW (window), box); + + gtk_window_present (GTK_WINDOW (window)); +} + +void +shutdown (GtkApplication *app, + gpointer user_data) +{ + if (!inited) + { + free (buttons); + free (labels); + } +} + +int +main (int argc, char **argv) +{ + hs_init (&argc, &argv); + + GtkApplication *app; + int res; + + app = gtk_application_new (APP_ID, G_APPLICATION_DEFAULT_FLAGS); + g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); + g_signal_connect (app, "shutdown", G_CALLBACK (shutdown), NULL); + res = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + + return res; +} diff --git a/unicode.hs b/unicode.hs new file mode 100644 index 0000000..cd58fec --- /dev/null +++ b/unicode.hs @@ -0,0 +1,37 @@ +{-# LANGUAGE ForeignFunctionInterface,CPP #-} + +module Unicode where + +import Data.List +import Data.Maybe +import qualified Data.Text as T +import qualified Data.Text.IO as IO +import Foreign.C.String +import Numeric +import System.Environment.Blank +import System.IO.Unsafe + +unicodeDataPath = (getEnv "XDG_DATA_HOME") >>= \s -> return ( + (case s of + Nothing -> "~/.local/share" + Just x -> if x == "" then "~/.local/share" else x) + ++ "/pickme/UnicodeData.txt") + +scsvField :: Int -> T.Text -> T.Text +scsvField n t = (T.splitOn (T.pack ";") t)!!n + +unicodeCharacters :: IO [T.Text] +unicodeCharacters = unicodeDataPath >>= IO.readFile >>= return . T.lines + +characterNames :: IO [T.Text] +characterNames = unicodeCharacters >>= \cs -> return $ map (scsvField 1) cs + +searchCharacterByName :: CString -> IO CString +searchCharacterByName s = peekCString s >>= \z -> + characterNames >>= \cns -> + unicodeCharacters >>= \ucs -> + newCString $ map + (\x -> toEnum . fst . (!!0) . readHex $ T.unpack (ucs!!(fromJust $ elemIndex x cns))) + (filter (\s' -> T.isInfixOf ((T.toUpper . T.pack) z) s') cns) + +foreign export ccall searchCharacterByName :: CString -> IO CString |