GhostLLM : RewriteText now works on GtkEntry!
Table of contents
Hello,
This is part three of my GhostLLM project. This project is about integrating “Rewrite Text” functionality natively into GTK3 widgets. Check the part one and part two in case you missed it.
Theory
We will be working with gtkentry.c
for this part. The goal is to implement ghostllm_rewrite_text_cb
callback function to replace the user selected text.
The idea is to declare new files - ghostllm.c
& the header file ghostllm.h
to deal with DBus related communications. We also update meson.build
file to include them in the build system.
Implementation
The code lives on GitHub repo here. Please refer the repo link for latest changes :)
Here’s the relevant code:
ghostllm.h:
// This is the only line in this file!
char* ghostllm_rewrite_text(char* input_str);
ghostllm.c
#include "config.h"
#include <gtk/gtk.h>
#include <gio/gio.h>
char *ghostllm_rewrite_text(char *input_str) {
// Connect to DBus Session Bus
GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
// Define the remote service and object path
char *service_name = "in.suryatejak.ghostllm";
char *object_path = "/in/suryatejak/ghostllm";
// This is required for communicating with DBus.
GDBusMessage *call_message = NULL;
GDBusMessage *reply_message = NULL;
GError **error = NULL;
// Define the DBus message to be sent.
call_message = g_dbus_message_new_method_call(service_name, object_path,
service_name, "RewriteText");
// Set arguments
g_dbus_message_set_body(call_message, g_variant_new("(s)", input_str));
// Call the RewriteText method on the remote service
reply_message = g_dbus_connection_send_message_with_reply_sync(
conn, call_message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL,
error);
// Retrieve the response data from DBus
GDBusMessageType reply_type = g_dbus_message_get_message_type(reply_message);
if (reply_type == G_DBUS_MESSAGE_TYPE_ERROR) {
// TODO: show error to user
g_dbus_message_to_gerror(reply_message, error);
return NULL;
}
if (reply_type == G_DBUS_MESSAGE_TYPE_METHOD_RETURN) {
// We got something!
GVariant *response = g_dbus_message_get_body(reply_message);
/**
* We receive a tuple which contains required string.
* Now, we fetch the first item and extract the string.
*/
gsize index = {0};
response = g_variant_get_child_value(response, index);
if (response == NULL) {
return NULL;
}
char *msg = g_variant_get_string(response, NULL);
return msg;
}
return NULL;
}
As seen above, we call our RewriteText
method with the user’s selected text. It returns a tuple (DBus type: (s)
) from which we extract rewritten string.
gtkentry.c:
static void
// If you've seen this function in part one, you'll
// notice the change in function arguments.
ghostllm_rewrite_text_cb (GtkEntry *entry)
{
// Get selection bounds for string
GtkEditable *editable = GTK_EDITABLE (entry);
gint start, end;
gtk_editable_get_selection_bounds (editable, &start, &end);
// Get selected text from selection bounds
gchar *input_str = _gtk_entry_get_display_text (entry, start, end);
// Rewrite the input string
char* response = ghostllm_rewrite_text(input_str);
if (response == NULL) {
g_printerr("Unable to generate GhostLLM response.");
return;
}
// HACK: Delete user selected text using "Cut" operation
gtk_entry_cut_clipboard(entry);
// Insert rewritten text
gtk_entry_insert_text(editable, response, strlen(response), &start);
g_free (response);
}
In this step, we simply generate new text from our server, then copy the user’s selected input text to the clipboard and lastly insert this new text at the cursor position.
It might take a few seconds depending on your machine but in the end, it works. I noticed this still works even when the window is inactive.
Demo
User Input:
Ollama response:
As you can see, the output has changed. Now, this is just to demonstrate something like this is doable even when it isn’t practical.
I was worried about the additional work involved in replacing the user selected text with the rewritten text but, as it turns out, I can cheat a little here using clipboard. :)
Next Steps
The idea is to implement this feature to GtkTextView
widget which was the original goal of this project. I will keep the GtkEntry
changes as-is for my amusement.
Conclusion
Thanks for your time!
This was a very fun project however I think the next part would mark the end for this. As always, the code will live on my GitHub.
I hope you liked this post. Please give it a Like to show your appreciation. If you feel there’s something I missed or can do better, let me know in the comments.
Bye for now :-)