#include #include #include #define LEN(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) /* Returns the current time in seconds and milliseconds since the epoch. * Mostly useful for calculating time intervals. */ static void aeGetTime(long *seconds, long *milliseconds) { struct timeval tv; gettimeofday(&tv, NULL); *seconds = tv.tv_sec; *milliseconds = tv.tv_usec/1000; } GtkWidget *text_widget; GtkTextTag *tag; #define LEFT_ARROW "\xe2\x86\x90" #define CLEAR_SCREEN "\xe2\x8e\x9a" #define SOUND "\xf0\x9f\x94\x8a" #define DRINK "\xf0\x9f\x9a\xb0" #define FOOD "\xf0\x9f\x8d\xb2" #define TIRED "\xf0\x9f\x98\xab" #define HAPPY "\xf0\x9f\x98\x80" #define LAUGHING "\xf0\x9f\x98\x84" #define NEWLINE "\xe2\x86\xb5" const char *alphabet[] = { DRINK, FOOD, HAPPY, LAUGHING, TIRED, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", LEFT_ARROW, CLEAR_SCREEN, SOUND, "_", NEWLINE, "!", "?", "." }; char buf[100000]; /* The current alphabetical letter. */ int current_letter = 0; /* The current position in the buffer. * * FIXME: What to do if we run out of buffer space?. */ int current_position = 0; /* Time the mouse was clicked (seconds) */ long pressed_time_sec = 0; /* Time the mouse was clicked (milliseconds) */ long pressed_time_msec = 0; /* Time the mouse was released (seconds) */ long released_time_sec = 0; /* Time the mouse was released (milliseconds) */ long released_time_msec = 0; static int pressed(GtkWidget *widget, gpointer data) { aeGetTime(&pressed_time_sec,&pressed_time_msec); return TRUE; } static void write_buf(void) { GtkTextBuffer *text_buffer; GtkTextIter start, end; text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_widget)); sprintf(buf+current_position,"%s",alphabet[current_letter % LEN(alphabet)]); gtk_text_buffer_set_text(text_buffer, buf, -1); gtk_text_buffer_get_iter_at_offset(text_buffer, &start, g_utf8_strlen(buf,-1)-1); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, -1); gtk_text_buffer_apply_tag(text_buffer, tag, &start, &end); } static int released(GtkWidget *widget, gpointer data) { GtkTextBuffer *text_buffer; GtkTextIter start, end; const char *current_char; aeGetTime(&released_time_sec,&released_time_msec); long long diff = (released_time_sec - pressed_time_sec)*1000 + (released_time_msec - pressed_time_msec); if (diff > 10000) { current_position = 0; current_letter = 0; } else if (diff > 500) { current_char = alphabet[current_letter % LEN(alphabet)]; if (!strcmp(current_char,LEFT_ARROW)) { if (current_position > 0) { text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_widget)); gtk_text_buffer_set_text(text_buffer, buf, -1); gtk_text_buffer_get_iter_at_offset(text_buffer, &start, 0); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, g_utf8_strlen(buf,-1)-2); current_position = strlen(gtk_text_buffer_get_slice(text_buffer,&start,&end,TRUE)); } } else if (!strcmp(current_char,CLEAR_SCREEN)) { current_position = 0; current_letter = 0; } else if (!strcmp(current_char,SOUND)) { char cmd[1000000]; buf[current_position] = '\0'; sprintf(cmd, "gtts-cli \"%s\" --output vito.mp3", buf); if (!system(cmd)) { system("mpg123 vito.mp3"); system("rm vito.mp3"); } } else if (!strcmp(current_char,"_")) { sprintf(buf+current_position," "); current_position += strlen(current_char); } else if (!strcmp(current_char,NEWLINE)) { sprintf(buf+current_position,"\n"); current_position += 1; } else { sprintf(buf+current_position,"%s",current_char); current_position += strlen(current_char); } if (current_position > LEN(buf) - 1) { /* FIXME: better way to do this? */ current_position = 0; } /* Uncomment the next line to start over at the beginning of the * alphabet after a new letter is entered. But, I think it's better * to keep the current letter. The reason is that you might want to * delete a whole word and so when you navigate to the backspace you * just have to keep entering the same letter. */ //current_letter = 0; } else { current_letter += 1; } write_buf(); return TRUE; } static void activate(GtkApplication *app, gpointer user_data) { GtkWidget *window; GtkTextBuffer *text_buffer; GtkCssProvider *provider; GtkStyleContext *context; GtkGesture *gesture; window = gtk_application_window_new(app); gtk_window_set_title(GTK_WINDOW(window), "Window"); gtk_window_set_default_size(GTK_WINDOW(window), 200, 200); gesture = gtk_gesture_click_new(); g_signal_connect(gesture, "pressed", G_CALLBACK(pressed), NULL); g_signal_connect(gesture, "released", G_CALLBACK(released), NULL); gtk_widget_add_controller(window, GTK_EVENT_CONTROLLER(gesture)); text_widget = gtk_text_view_new(); gtk_text_view_set_editable((GtkTextView *) text_widget, FALSE); gtk_text_view_set_cursor_visible((GtkTextView *) text_widget, TRUE); gtk_text_view_set_wrap_mode((GtkTextView *) text_widget, GTK_WRAP_CHAR); gtk_widget_set_can_focus(text_widget, FALSE); gtk_widget_set_can_target(text_widget, FALSE); text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_widget)); /* Change default font and color throughout the widget */ provider = gtk_css_provider_new(); gtk_css_provider_load_from_data(provider, "textview {" " font: 144pt monospace;" " color: black;" "}", -1); context = gtk_widget_get_style_context(text_widget); gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); /* Use a tag to change the color for just one part of the widget */ tag = gtk_text_buffer_create_tag(text_buffer, "red_foreground", "foreground", "red", NULL); buf[0] = 'a'; buf[1] = '\0'; write_buf(); gtk_window_set_child(GTK_WINDOW(window), text_widget); gtk_window_fullscreen(GTK_WINDOW(window)); gtk_window_present(GTK_WINDOW(window)); } int main(int argc, char **argv) { GtkApplication *app; int status; app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); return status; }