summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaura Aino Violetta Aléanor <lav@vampires.gay>2023-05-07 19:33:43 +0200
committerLaura Aino Violetta Aléanor <lav@vampires.gay>2023-05-07 19:33:43 +0200
commitc9c7476a205bc7b30f6d7f218a1d794127f1bf2f (patch)
tree09d4b2978570f4b0e51a8a673598c8fc5d337452
downloadpickme-c9c7476a205bc7b30f6d7f218a1d794127f1bf2f.tar.gz
pickme-c9c7476a205bc7b30f6d7f218a1d794127f1bf2f.zip
Initial commit
-rw-r--r--Makefile21
-rw-r--r--config.h1
-rw-r--r--pickme.c157
-rw-r--r--unicode.hs37
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