We know where the text caret is
This commit is contained in:
commit
5ec169278f
24
meson.build
Normal file
24
meson.build
Normal file
|
@ -0,0 +1,24 @@
|
|||
project('at-emoji', 'c',
|
||||
version: '0.0.1',
|
||||
meson_version: '>= 1.0.0',
|
||||
)
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
ate_dependencies = [
|
||||
dependency('atspi-2', version: '>= 2.50.0'),
|
||||
dependency('glib-2.0', version: '>=2.44'),
|
||||
dependency('gtk+-3.0', version: '>=3.24.43'),
|
||||
cc.find_library('m', required: false)
|
||||
]
|
||||
|
||||
ate_sources = [
|
||||
'src/main.c',
|
||||
]
|
||||
|
||||
executable('at-emoji',
|
||||
sources: ate_sources,
|
||||
dependencies: ate_dependencies,
|
||||
install: true
|
||||
)
|
||||
|
234
src/main.c
Normal file
234
src/main.c
Normal file
|
@ -0,0 +1,234 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <atspi/atspi.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
AtspiAccessible *find_active_window()
|
||||
{
|
||||
AtspiAccessible *active = NULL;
|
||||
|
||||
for (gint x = 0; x < atspi_get_desktop_count(); x++) {
|
||||
|
||||
AtspiAccessible *desktop = atspi_get_desktop(x);
|
||||
|
||||
for (gint i = 0; i < atspi_accessible_get_child_count(desktop, NULL); i++) {
|
||||
|
||||
AtspiAccessible *app = atspi_accessible_get_child_at_index(desktop, i, NULL);
|
||||
|
||||
g_print("\t%s: %d windows\n", atspi_accessible_get_name(app, NULL),
|
||||
atspi_accessible_get_child_count(app, NULL));
|
||||
|
||||
for (gint j = 0; j < atspi_accessible_get_child_count(app, NULL); j++) {
|
||||
|
||||
AtspiAccessible *wnd = atspi_accessible_get_child_at_index(app, j, NULL);
|
||||
|
||||
AtspiStateSet *wst = atspi_accessible_get_state_set(wnd);
|
||||
|
||||
if (atspi_state_set_contains(wst, ATSPI_STATE_ACTIVE)) {
|
||||
g_print("Active window: %s\n", atspi_accessible_get_name(app, NULL), 0);
|
||||
active = wnd;
|
||||
g_object_unref(wst);
|
||||
g_object_unref(app);
|
||||
g_object_unref(desktop);
|
||||
return active;
|
||||
}
|
||||
|
||||
g_object_unref(wst);
|
||||
g_object_unref(wnd);
|
||||
}
|
||||
|
||||
g_object_unref(app);
|
||||
}
|
||||
|
||||
g_object_unref(desktop);
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
AtspiAccessible *find_focused_text_BAD(AtspiAccessible *active)
|
||||
{
|
||||
|
||||
AtspiStateSet *wst = atspi_accessible_get_state_set(active);
|
||||
|
||||
if (!(atspi_state_set_contains(wst, ATSPI_STATE_SHOWING) &&
|
||||
atspi_state_set_contains(wst, ATSPI_STATE_VISIBLE))) {
|
||||
g_object_unref(wst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_object_unref(wst);
|
||||
|
||||
for (gint j = 0; j < atspi_accessible_get_child_count(active, NULL); j++) {
|
||||
|
||||
AtspiAccessible *chld = atspi_accessible_get_child_at_index(active, j, NULL);
|
||||
|
||||
AtspiText *txt = atspi_accessible_get_text_iface(chld);
|
||||
|
||||
if (txt) {
|
||||
g_object_unref(txt);
|
||||
return chld;
|
||||
}
|
||||
|
||||
AtspiAccessible *x = find_focused_text_BAD(chld);
|
||||
|
||||
if (x) {
|
||||
g_object_unref(chld);
|
||||
return x;
|
||||
}
|
||||
|
||||
g_object_unref(chld);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AtspiAccessible *find_focused_text(AtspiAccessible *active)
|
||||
{
|
||||
AtspiCollection *things = atspi_accessible_get_collection_iface(active);
|
||||
|
||||
if (!things) { // Qt is bad
|
||||
g_print("No Collection implemented! BAD search engaged\n");
|
||||
return find_focused_text_BAD(active);
|
||||
}
|
||||
|
||||
AtspiAccessible *text = NULL;
|
||||
g_print("(application.length)=(%d)\n", atspi_accessible_get_child_count(active, NULL));
|
||||
|
||||
if (things) {
|
||||
AtspiStateType appd_state = ATSPI_STATE_FOCUSED;
|
||||
GArray *match_states = g_array_new(TRUE, TRUE, sizeof(appd_state));
|
||||
g_array_append_val(match_states, appd_state);
|
||||
|
||||
gchar *appd_iface = "Text";
|
||||
GArray *match_ifaces = g_array_new(TRUE, TRUE, sizeof(appd_iface));
|
||||
g_array_append_val(match_ifaces, appd_iface);
|
||||
|
||||
AtspiStateSet *match_stateset = atspi_state_set_new(match_states);
|
||||
g_array_unref(match_states);
|
||||
|
||||
AtspiMatchRule *match_rule = atspi_match_rule_new(
|
||||
match_stateset, ATSPI_Collection_MATCH_ALL, NULL, ATSPI_Collection_MATCH_ANY, NULL,
|
||||
ATSPI_Collection_MATCH_ANY, match_ifaces, ATSPI_Collection_MATCH_ALL, FALSE);
|
||||
g_array_unref(match_ifaces);
|
||||
|
||||
GArray *matches = atspi_collection_get_matches(things, match_rule,
|
||||
ATSPI_Collection_SORT_ORDER_CANONICAL, 0, TRUE, NULL);
|
||||
g_object_unref(match_rule);
|
||||
|
||||
g_print("(#matches)=(%d)\n", matches->len);
|
||||
|
||||
if (!matches->len) {
|
||||
g_array_unref(matches);
|
||||
return text;
|
||||
}
|
||||
|
||||
text = g_array_index(matches, AtspiAccessible *, 0);
|
||||
g_array_unref(matches);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
AtspiPoint get_text_caret_pos(AtspiAccessible *text)
|
||||
{
|
||||
AtspiText *tiface = atspi_accessible_get_text_iface(text);
|
||||
|
||||
gint caret_pos = atspi_text_get_caret_offset(tiface, NULL);
|
||||
|
||||
AtspiRect *trect =
|
||||
atspi_text_get_range_extents(tiface, caret_pos, caret_pos + 1, ATSPI_COORD_TYPE_SCREEN, NULL);
|
||||
|
||||
gint x = trect->x, y = trect->y;
|
||||
g_free(trect);
|
||||
|
||||
if (x == 0 && y == 0) { // Qt is bad
|
||||
g_print("Text returned bogus caret position! Trying inward extent.\n");
|
||||
AtspiRect *trect =
|
||||
atspi_text_get_range_extents(tiface, caret_pos - 1, caret_pos, ATSPI_COORD_TYPE_SCREEN, NULL);
|
||||
x = trect->x + trect->width;
|
||||
y = trect->y;
|
||||
|
||||
gchar *txt = atspi_text_get_text(tiface, caret_pos - 1, caret_pos, NULL);
|
||||
|
||||
if (g_strcmp0(txt, "\n") == 0) {
|
||||
AtspiRect *trect1 =
|
||||
atspi_text_get_range_extents(tiface, 0, caret_pos - 1, ATSPI_COORD_TYPE_SCREEN, NULL);
|
||||
x = trect1->x;
|
||||
y = trect->y + trect->height;
|
||||
|
||||
g_free(trect1);
|
||||
}
|
||||
|
||||
g_free(txt);
|
||||
g_free(trect);
|
||||
}
|
||||
|
||||
g_object_unref(tiface);
|
||||
|
||||
if (x == 0 && y == 0) { // Qt is bad
|
||||
g_print("Text returned bogus caret position (again)! Trying component position.\n");
|
||||
AtspiComponent *cface = atspi_accessible_get_component_iface(text);
|
||||
|
||||
AtspiPoint *point = atspi_component_get_position(cface, ATSPI_COORD_TYPE_SCREEN, NULL);
|
||||
x = point->x;
|
||||
y = point->y;
|
||||
|
||||
g_free(point);
|
||||
g_object_unref(cface);
|
||||
}
|
||||
|
||||
return (AtspiPoint){x, y};
|
||||
}
|
||||
|
||||
static void activate(GtkApplication *app, gpointer user_data)
|
||||
{
|
||||
GtkWidget *window;
|
||||
|
||||
window = gtk_window_new(GTK_WINDOW_POPUP);
|
||||
gtk_window_set_title(GTK_WINDOW(window), "Window");
|
||||
gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
|
||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_NONE);
|
||||
gint w, h;
|
||||
gtk_window_get_size(GTK_WINDOW(window), &w, &h);
|
||||
printf("%i, %i\n", w, h);
|
||||
g_print("scanning apps...\n");
|
||||
|
||||
AtspiAccessible *active = find_active_window();
|
||||
|
||||
if (!active) {
|
||||
g_print("No active window! skedaddling outta here\n");
|
||||
return;
|
||||
}
|
||||
|
||||
AtspiAccessible *text = find_focused_text(active);
|
||||
|
||||
if (!text) {
|
||||
g_print("No focused text! skedaddling outta here\n");
|
||||
return;
|
||||
}
|
||||
|
||||
AtspiPoint trect = get_text_caret_pos(text);
|
||||
|
||||
g_print("(x,y)=(%d,%d)\n", trect.x, trect.y);
|
||||
gtk_window_move(GTK_WINDOW(window), trect.x - w / 2, trect.y - h);
|
||||
|
||||
g_object_unref(active);
|
||||
|
||||
gtk_widget_show_all(window);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
atspi_init();
|
||||
|
||||
GtkApplication *app = gtk_application_new("net.kimapr.AtEmoji", G_APPLICATION_FLAGS_NONE);
|
||||
g_application_hold(G_APPLICATION(app));
|
||||
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
|
||||
int status = g_application_run(G_APPLICATION(app), argc, argv);
|
||||
g_object_unref(app);
|
||||
|
||||
return status;
|
||||
}
|
Loading…
Reference in a new issue