#include "osglobals_gtk.inl"
#include "oscontrol_gtk.inl"
#include "../osglobals.h"
#include "../osgui.inl"
#include <draw2d/color.h>
#include <draw2d/font.h>
#include <draw2d/image.h>
#include <core/event.h>
#include <core/heap.h>
#include <core/strings.h>
#include <core/stream.h>
#include <osbs/log.h>
#include <sewer/cassert.h>
#include <sewer/unicode.h>
#if !defined(__GTK3__)
#error This file is only for GTK Toolkit
#endif
static bool_t i_IMPOSTOR_MAPPED = FALSE;
static GtkWidget *kWINDOW = NULL;
static GtkWidget *kLABEL = NULL;
static GtkWidget *kENTRY = NULL;
static GtkWidget *kBUTTON = NULL;
static GtkWidget *kCHECK[10] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static GtkWidget *kSEPARATOR = NULL;
static GtkWidget *kLINKBUTTON = NULL;
static GtkWidget *kPROGRESSBAR = NULL;
static GtkWidget *kFRAME = NULL;
static GtkWidget *kTABLE = NULL;
static GtkWidget *kSCROLLED = NULL;
static GtkWidget *kHEADER = NULL;
static GtkWidget *kRESTORE_FOCUS_WINDOW = NULL;
static GtkWidget *kRESTORE_FOCUS_WIDGET = NULL;
static GdkPixbuf *kCHECKSBITMAP = NULL;
static uint32_t kCHECK_WIDTH = 0;
static uint32_t kCHECK_HEIGHT = 0;
static uint32_t kENTRY_HEIGHT = 0;
static uint32_t kPROGRESS_HEIGHT = 0;
static uint32_t kSCROLLBAR_HEIGHT = 0;
static bool_t kDARK_MODE = FALSE;
static color_t kLABEL_COLOR = 0;
static color_t kVIEW_COLOR = 0;
static color_t kLINE_COLOR = 0;
static color_t kLINK_COLOR = 0;
static color_t kBORD_COLOR = 0;
static color_t kBORDFOCUS_COLOR = 0;
static color_t kTEXT_COLOR = 0;
static color_t kSELTX_COLOR = 0;
static color_t kHOTTX_COLOR = 0;
static color_t kTEXTBACKDROP_COLOR = 0;
static color_t kSELTXBACKDROP_COLOR = 0;
static color_t kHOTTXBACKDROP_COLOR = 0;
static String *kCSS_ENTRY = NULL;
static String *kCSS_BUTTON = NULL;
static String *kCSS_RADIO = NULL;
static String *kCSS_CHECK = NULL;
static String *kCSS_COMBOBOX = NULL;
static String *kCSS_FRAME = NULL;
static String *kCSS_TEXTVIEW = NULL;
static String *kCSS_TEXTVIEWTEXT = NULL;
static String *kCSS_PROGRESSBAR = NULL;
const uint32_t kBUTTON_VPADDING = 8;
const uint32_t kBUTTON_HPADDING = 16;
const uint32_t kPOPUP_VPADDING = 8;
const uint32_t kPOPUP_HPADDING = 16;
const uint32_t kENTRY_VPADDING = 8;
const uint32_t kENTRY_HPADDING = 8;
const uint32_t kBUTTON_IMAGE_SEP = 4;
const uint32_t kCHECKBOX_IMAGE_SEP = 8;
static void i_widget_margins(GtkWidget *widget, const uint32_t max_width, const uint32_t max_height, uint32_t *x, uint32_t *y, uint32_t *width, uint32_t *height, uint32_t *color)
{
uint32_t i, j;
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, max_width, max_height);
cairo_t *cairo = cairo_create(surface);
GdkPixbuf *bitmap = NULL;
uint32_t *buffer = NULL;
uint32_t x0 = max_width, x1 = 0, y0 = max_height, y1 = 0;
gtk_widget_draw(widget, cairo);
bitmap = gdk_pixbuf_get_from_surface(surface, 0, 0, max_width, max_height);
cassert(gdk_pixbuf_get_rowstride(bitmap) % 4 == 0);
buffer = cast(gdk_pixbuf_get_pixels(bitmap), uint32_t);
*color = 0;
for (i = 0; i < max_width; ++i)
for (j = 0; j < max_height; ++j)
{
if (buffer[j * max_width + i] != 0)
{
if (i < x0)
x0 = i;
if (i > x1)
x1 = i;
if (j < y0)
y0 = j;
if (j > y1)
y1 = j;
if (*color == 0)
*color = buffer[j * max_width + i];
}
}
cairo_surface_destroy(surface);
cairo_destroy(cairo);
g_object_unref(bitmap);
*x = x0;
*y = y0;
*width = x1 - x0 + 1;
*height = y1 - y0 + 1;
}
static color_t i_from_gdkcolor(const GdkRGBA *gdkcolor)
{
return color_rgba(
(uint8_t)(gdkcolor->red * 255.),
(uint8_t)(gdkcolor->green * 255.),
(uint8_t)(gdkcolor->blue * 255.),
(uint8_t)(gdkcolor->alpha * 255.));
}
static color_t i_color_prop(GtkWidget *widget, const char_t *prop, GtkStateFlags flags)
{
GValue value = G_VALUE_INIT;
GdkRGBA *gdkcolor = NULL;
color_t color = 0;
GtkStyleContext *c = gtk_widget_get_style_context(widget);
gtk_style_context_get_property(c, prop, flags, &value);
gdkcolor = cast(g_value_get_boxed(&value), GdkRGBA);
color = i_from_gdkcolor(gdkcolor);
g_value_unset(&value);
return color;
}
static color_t i_frame_color(GtkWidget *widget, const uint32_t size, const bool_t middle_i, const bool_t middle_j)
{
uint32_t i = 0, j = 0;
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size, size);
cairo_t *cairo = cairo_create(surface);
GdkPixbuf *bitmap = NULL;
uint32_t *buffer = NULL;
color_t col = 0;
gtk_widget_draw(widget, cairo);
bitmap = gdk_pixbuf_get_from_surface(surface, 0, 0, size, size);
cassert(gdk_pixbuf_get_rowstride(bitmap) % 4 == 0);
buffer = cast(gdk_pixbuf_get_pixels(bitmap), uint32_t);
if (middle_i == TRUE)
i = size / 2;
if (middle_j == TRUE)
j = size / 2;
for (; i < size && col == 0; ++i)
for (; j < size && col == 0; ++j)
{
if (buffer[j * size + i] != 0)
col = buffer[j * size + i];
}
cairo_surface_destroy(surface);
cairo_destroy(cairo);
g_object_unref(bitmap);
return col;
}
static void i_precompute_colors(void)
{
real32_t r = 0, g = 0, b = 0;
cassert(kLABEL_COLOR == 0);
cassert(i_IMPOSTOR_MAPPED == TRUE);
kLABEL_COLOR = i_color_prop(kLABEL, "color", GTK_STATE_FLAG_ACTIVE);
kVIEW_COLOR = i_color_prop(kWINDOW, "background-color", GTK_STATE_FLAG_NORMAL);
kLINE_COLOR = i_frame_color(kFRAME, 20, FALSE, TRUE);
kBORDFOCUS_COLOR = i_frame_color(kPROGRESSBAR, kPROGRESS_HEIGHT, TRUE, TRUE);
kLINK_COLOR = i_color_prop(gtk_bin_get_child(GTK_BIN(kLINKBUTTON)), "color", GTK_STATE_FLAG_ACTIVE);
kBORD_COLOR = kLINE_COLOR;
kTEXT_COLOR = i_color_prop(kTABLE, "color", GTK_STATE_FLAG_NORMAL);
kSELTX_COLOR = i_color_prop(kTABLE, "color", GTK_STATE_FLAG_SELECTED);
kHOTTX_COLOR = i_color_prop(kTABLE, "color", GTK_STATE_FLAG_PRELIGHT);
kTEXTBACKDROP_COLOR = i_color_prop(kTABLE, "color", GTK_STATE_FLAG_NORMAL | GTK_STATE_FLAG_BACKDROP);
kSELTXBACKDROP_COLOR = i_color_prop(kTABLE, "color", GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_BACKDROP);
kHOTTXBACKDROP_COLOR = i_color_prop(kTABLE, "color", GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_BACKDROP);
r = (real32_t)((uint8_t)(kVIEW_COLOR) / 255.f);
g = (real32_t)((uint8_t)(kVIEW_COLOR >> 8) / 255.f);
b = (real32_t)((uint8_t)(kVIEW_COLOR >> 16) / 255.f);
kDARK_MODE = (.21 * r + .72 * g + .07 * b) < .5 ? TRUE : FALSE;
}
static bool_t i_equal_images(GdkPixbuf *pixbuf1, const uint32_t x1, const uint32_t y1, GdkPixbuf *pixbuf2, const uint32_t x2, const uint32_t y2, const uint32_t width, const uint32_t height)
{
gboolean alpha1 = gdk_pixbuf_get_has_alpha(pixbuf1);
gboolean alpha2 = gdk_pixbuf_get_has_alpha(pixbuf2);
if (alpha1 == alpha2)
{
const guchar *pixels1 = gdk_pixbuf_get_pixels(pixbuf1);
const guchar *pixels2 = gdk_pixbuf_get_pixels(pixbuf2);
uint32_t i, j, offset = alpha1 ? 4 : 3;
int stride1 = gdk_pixbuf_get_rowstride(pixbuf1);
int stride2 = gdk_pixbuf_get_rowstride(pixbuf2);
pixels1 += y1 * stride1;
pixels2 += y2 * stride2;
for (j = 0; j < height; ++j)
{
for (i = 0; i < width; ++i)
{
const guchar *pix1 = pixels1 + (x1 + i) * offset;
const guchar *pix2 = pixels2 + (x2 + i) * offset;
if (pix1[0] != pix2[0] || pix1[1] != pix2[1] || pix1[2] != pix2[2])
return FALSE;
}
pixels1 += stride1;
pixels2 += stride2;
}
return TRUE;
}
return FALSE;
}
static void i_precompute_checks(void)
{
uint32_t mx, my, mwidth, mheight, border = 0;
gdouble dwidth = 0, dheight = 0;
cairo_surface_t *surface = NULL;
cairo_t *cairo = NULL;
uint32_t i = 0;
cassert(kCHECK_WIDTH == 0);
cassert(kCHECK_HEIGHT == 0);
cassert(kCHECKSBITMAP == NULL);
cassert(i_IMPOSTOR_MAPPED == TRUE);
i_widget_margins(kCHECK[0], 30, 30, &mx, &my, &mwidth, &mheight, &border);
dwidth = (gdouble)mwidth;
dheight = (gdouble)mheight;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10 * mwidth, mheight);
cairo = cairo_create(surface);
cairo_translate(cairo, -(gdouble)mx, -(gdouble)my);
for (i = 0; i < 10; ++i)
{
gtk_widget_draw(kCHECK[i], cairo);
cairo_translate(cairo, dwidth, 0);
}
kCHECK_WIDTH = mwidth;
kCHECK_HEIGHT = mheight;
kCHECKSBITMAP = gdk_pixbuf_get_from_surface(surface, 0, 0, 10 * mwidth, mheight);
if (i_equal_images(kCHECKSBITMAP, 0, 0, kCHECKSBITMAP, 5 * mwidth, 0, mwidth, mheight) == TRUE)
{
g_object_unref(kCHECKSBITMAP);
cairo_translate(cairo, -dwidth * 5, 0);
for (i = 5; i < 10; ++i)
{
GtkStyleContext *context = gtk_widget_get_style_context(kCHECK[i]);
gtk_render_check(context, cairo, 0, 0, dwidth, dheight);
cairo_translate(cairo, dwidth, 0);
}
kCHECKSBITMAP = gdk_pixbuf_get_from_surface(surface, 0, 0, 10 * mwidth, mheight);
}
cairo_surface_destroy(surface);
cairo_destroy(cairo);
}
static void i_precompute_scroll(void)
{
GtkAllocation alloc;
GtkWidget *scrollbar = gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(kSCROLLED));
cassert(kSCROLLBAR_HEIGHT == 0);
cassert(i_IMPOSTOR_MAPPED == TRUE);
gtk_widget_get_allocation(scrollbar, &alloc);
kSCROLLBAR_HEIGHT = alloc.width;
if (kSCROLLBAR_HEIGHT < 2)
kSCROLLBAR_HEIGHT = 2;
else if (kSCROLLBAR_HEIGHT < 12)
kSCROLLBAR_HEIGHT = 12;
}
static gboolean i_OnWindowDamage(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
cassert(widget == kWINDOW);
unref(event);
unref(data);
if (i_IMPOSTOR_MAPPED == TRUE)
return FALSE;
i_IMPOSTOR_MAPPED = TRUE;
{
Font *font = _osgui_create_default_font();
real32_t width, height;
cassert(kENTRY_HEIGHT == 0);
cassert(kPROGRESS_HEIGHT == 0);
font_extents(font, "OO", -1, &width, &height);
kENTRY_HEIGHT = (uint32_t)height + kENTRY_VPADDING;
kPROGRESS_HEIGHT = (uint32_t)height;
unref(width);
font_destroy(&font);
}
i_precompute_colors();
i_precompute_checks();
i_precompute_scroll();
cassert(kHEADER == NULL);
{
GtkTreeViewColumn *column = gtk_tree_view_get_column(GTK_TREE_VIEW(kTABLE), 0);
kHEADER = gtk_tree_view_column_get_button(GTK_TREE_VIEW_COLUMN(column));
}
gtk_widget_hide(widget);
return FALSE;
}
static void i_impostor_window(void)
{
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
GtkWidget *hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *tview = gtk_text_view_new();
GtkCellRenderer *renderer = NULL;
GtkTreeViewColumn *column1 = NULL;
GtkTreeViewColumn *column2 = NULL;
GtkListStore *store = NULL;
cassert(kWINDOW == NULL);
kWINDOW = gtk_offscreen_window_new();
kLABEL = gtk_label_new("__LABEL__");
kENTRY = gtk_entry_new();
kBUTTON = gtk_button_new();
kSEPARATOR = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
kLINKBUTTON = gtk_link_button_new_with_label("", "__LINK__");
kPROGRESSBAR = gtk_progress_bar_new();
kFRAME = gtk_frame_new(NULL);
kTABLE = gtk_tree_view_new();
kSCROLLED = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(kSCROLLED), tview);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(kSCROLLED), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(kPROGRESSBAR), 1.);
gtk_container_add(GTK_CONTAINER(kFRAME), kTABLE);
renderer = gtk_cell_renderer_text_new();
column1 = gtk_tree_view_column_new_with_attributes("Column1", renderer, "text", 0, NULL);
column2 = gtk_tree_view_column_new_with_attributes("Column2", renderer, "text", 0, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(kTABLE), column1);
gtk_tree_view_append_column(GTK_TREE_VIEW(kTABLE), column2);
gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column1), GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column1), 100);
gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column2), GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column2), 100);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(kTABLE), TRUE);
gtk_widget_set_size_request(kTABLE, (gint)200, (gint)100);
store = gtk_list_store_new(1, G_TYPE_STRING);
gtk_tree_view_set_model(GTK_TREE_VIEW(kTABLE), GTK_TREE_MODEL(store));
g_object_unref(store);
kCHECK[0] = gtk_check_button_new();
kCHECK[1] = gtk_check_button_new();
kCHECK[2] = gtk_check_button_new();
kCHECK[3] = gtk_check_button_new();
kCHECK[4] = gtk_check_button_new();
kCHECK[5] = gtk_check_button_new();
kCHECK[6] = gtk_check_button_new();
kCHECK[7] = gtk_check_button_new();
kCHECK[8] = gtk_check_button_new();
kCHECK[9] = gtk_check_button_new();
gtk_container_add(GTK_CONTAINER(kWINDOW), vbox);
gtk_box_pack_start(GTK_BOX(hbox1), kCHECK[0], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), kCHECK[1], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), kCHECK[2], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), kCHECK[3], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), kCHECK[4], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox2), kCHECK[5], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox2), kCHECK[6], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox2), kCHECK[7], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox2), kCHECK[8], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox2), kCHECK[9], FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), kBUTTON, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), kLABEL, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), kENTRY, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), kSEPARATOR, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), kLINKBUTTON, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), kPROGRESSBAR, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), kFRAME, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), kSCROLLED, TRUE, TRUE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[0]), FALSE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[1]), FALSE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[2]), FALSE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[3]), FALSE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[4]), FALSE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[5]), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[6]), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[7]), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[8]), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(kCHECK[9]), TRUE);
gtk_widget_set_state_flags(kCHECK[0], (GtkStateFlags)GTK_STATE_FLAG_NORMAL, FALSE);
gtk_widget_set_state_flags(kCHECK[1], (GtkStateFlags)GTK_STATE_PRELIGHT, FALSE);
gtk_widget_set_state_flags(kCHECK[2], (GtkStateFlags)GTK_STATE_SELECTED, FALSE);
gtk_widget_set_state_flags(kCHECK[3], (GtkStateFlags)(GTK_STATE_FLAG_BACKDROP | GTK_STATE_PRELIGHT), FALSE);
gtk_widget_set_sensitive(kCHECK[4], FALSE);
gtk_widget_set_state_flags(kCHECK[5], (GtkStateFlags)GTK_STATE_FLAG_NORMAL, FALSE);
gtk_widget_set_state_flags(kCHECK[6], (GtkStateFlags)GTK_STATE_PRELIGHT, FALSE);
gtk_widget_set_state_flags(kCHECK[7], (GtkStateFlags)GTK_STATE_SELECTED, FALSE);
gtk_widget_set_state_flags(kCHECK[8], (GtkStateFlags)(GTK_STATE_FLAG_BACKDROP | GTK_STATE_PRELIGHT), FALSE);
gtk_widget_set_sensitive(kCHECK[9], FALSE);
g_signal_connect(kWINDOW, "damage-event", G_CALLBACK(i_OnWindowDamage), NULL);
gtk_widget_show_all(kWINDOW);
}
device_t osglobals_device(const void *non_used)
{
cassert(FALSE);
cassert_unref(non_used == NULL, non_used);
return ekDESKTOP;
}
color_t osglobals_color(const syscolor_t *color)
{
cassert_no_null(color);
cassert(kWINDOW != NULL);
switch (*color)
{
case ekSYSCOLOR_DARKMODE:
return kDARK_MODE;
case ekSYSCOLOR_LABEL:
cassert(kLABEL_COLOR != 0);
return kLABEL_COLOR;
case ekSYSCOLOR_VIEW:
cassert(kVIEW_COLOR != 0);
return kVIEW_COLOR;
case ekSYSCOLOR_LINE:
cassert(kLINE_COLOR != 0);
return kLINE_COLOR;
case ekSYSCOLOR_LINK:
cassert(kLINK_COLOR != 0);
return kLINK_COLOR;
case ekSYSCOLOR_BORDER:
cassert(kBORD_COLOR != 0);
return kBORD_COLOR;
cassert_default();
}
return 0;
}
void osglobals_resolution(const void *non_used, real32_t *width, real32_t *height)
{
unref(non_used);
cassert_no_null(width);
cassert_no_null(height);
#if GTK_CHECK_VERSION(3, 22, 0)
{
GdkDisplay *display = gdk_display_get_default();
GdkMonitor *primary_monitor = gdk_display_get_primary_monitor(display);
GdkRectangle monitor_geometry;
gdk_monitor_get_geometry(primary_monitor, &monitor_geometry);
*width = (real32_t)monitor_geometry.width;
*height = (real32_t)monitor_geometry.height;
}
#else
*width = (real32_t)gdk_screen_width();
*height = (real32_t)gdk_screen_height();
#endif
}
void osglobals_mouse_position(const void *non_used, real32_t *x, real32_t *y)
{
gint ix, iy;
GdkDisplay *display = gdk_display_get_default();
GdkWindow *window = NULL;
GdkDevice *mouse_device = NULL;
cassert_no_null(x);
cassert_no_null(y);
unref(non_used);
#if GTK_CHECK_VERSION(3, 20, 0)
{
GdkSeat *seat = gdk_display_get_default_seat(display);
mouse_device = gdk_seat_get_pointer(seat);
}
#else
{
GdkDeviceManager *devman = gdk_display_get_device_manager(display);
mouse_device = gdk_device_manager_get_client_pointer(devman);
}
#endif
window = gdk_display_get_default_group(display);
gdk_window_get_device_position(window, mouse_device, &ix, &iy, NULL);
*x = (real32_t)ix;
*y = (real32_t)iy;
}
Cursor *osglobals_cursor(const gui_cursor_t cursor, const Image *image, const real32_t hot_x, const real32_t hot_y)
{
GdkDisplay *display = gdk_display_get_default();
GdkCursor *gdkcursor = NULL;
switch (cursor)
{
case ekGUI_CURSOR_ARROW:
gdkcursor = gdk_cursor_new_for_display(display, GDK_ARROW);
break;
case ekGUI_CURSOR_HAND:
gdkcursor = gdk_cursor_new_for_display(display, GDK_HAND2);
break;
case ekGUI_CURSOR_IBEAM:
gdkcursor = gdk_cursor_new_for_display(display, GDK_XTERM);
break;
case ekGUI_CURSOR_CROSS:
gdkcursor = gdk_cursor_new_for_display(display, GDK_FLEUR);
break;
case ekGUI_CURSOR_SIZEWE:
gdkcursor = gdk_cursor_new_for_display(display, GDK_SB_H_DOUBLE_ARROW);
break;
case ekGUI_CURSOR_SIZENS:
gdkcursor = gdk_cursor_new_for_display(display, GDK_SB_V_DOUBLE_ARROW);
break;
case ekGUI_CURSOR_USER:
{
const GdkPixbuf *pixbuf = image_native(image);
gdkcursor = gdk_cursor_new_from_pixbuf(display, (GdkPixbuf *)pixbuf, (gint)hot_x, (gint)hot_y);
break;
}
cassert_default();
}
heap_auditor_add("GdkCursor");
return (Cursor *)gdkcursor;
}
void osglobals_cursor_destroy(Cursor **cursor)
{
cassert_no_null(cursor);
cassert_no_null(*cursor);
g_object_unref((GdkCursor *)*cursor);
heap_auditor_delete("GdkCursor");
*cursor = NULL;
}
void osglobals_value(const uint32_t index, void *value)
{
cassert_no_null(value);
switch (index)
{
case 0:
*cast(value, uint32_t) = 0;
break;
case 1:
*cast(value, uint32_t) = 0;
break;
cassert_default();
}
}
void osglobals_transitions(void *nonused, const real64_t prtime, const real64_t crtime)
{
unref(nonused);
unref(prtime);
unref(crtime);
if (kRESTORE_FOCUS_WIDGET != NULL)
{
}
}
static gboolean i_OnIdle(Listener *listener)
{
listener_event(listener, ekGUI_EVENT_IDLE, NULL, NULL, NULL, void, void, void);
listener_destroy(&listener);
return FALSE;
}
void osglobals_OnIdle(void *nonused, Listener *listener)
{
unref(nonused);
g_idle_add((GSourceFunc)i_OnIdle, listener);
}
#define IS_BLANK(c) (bool_t)((c) == ' ' || (c) == '\n' || (c) == '\t' || (c) == '\v' || (c) == '\f' || (c) == '\r')
static const char *i_get_next_section(const char *css_data, char **pcss, int *n)
{
while (*(*pcss) != '{' && *(*pcss) != 0)
(*pcss)++;
if (*(*pcss) == '{')
{
char *st = *pcss;
char *ed = *pcss;
while (st > css_data)
{
st--;
if (*st == ';' || *st == '}')
{
st++;
break;
}
}
while (IS_BLANK(*st))
st++;
if (ed > st)
ed--;
while (IS_BLANK(*ed))
ed--;
if (ed > st || *st != '{')
*n = ed - st + 1;
else
*n = 0;
return st;
}
return NULL;
}
static void i_jump_next_section(char **pcss)
{
uint32_t i = 0;
cassert(*(*pcss) == '{');
for (;;)
{
if (*(*pcss) == 0)
return;
if (*(*pcss) == '{')
{
i++;
}
else if (*(*pcss) == '}')
{
cassert(i > 0);
i--;
if (i == 0)
{
(*pcss)++;
return;
}
}
(*pcss)++;
}
}
static bool_t i_section(const char_t *sect, const char_t *str, uint32_t *n)
{
const char_t *st = sect;
cassert_no_null(sect);
cassert_no_null(str);
cassert_no_null(n);
if (*sect == 0)
return FALSE;
if (*sect == '.')
sect++;
for (; *str != 0; ++str, ++sect)
{
if (*sect == 0)
return FALSE;
if (unicode_tolower((uint32_t)*sect) != unicode_tolower((uint32_t)*str))
return FALSE;
}
if (*sect == 0 || *sect == '.' || *sect == ':' || *sect == ' ')
{
*n = (uint32_t)(sect - st);
return TRUE;
}
return FALSE;
}
static void i_parse_gtk_theme(void)
{
GtkSettings *settings = NULL;
gchar *theme_name = NULL;
GtkCssProvider *prov = NULL;
char *css_data = NULL;
char *pcss = NULL;
const char *section = NULL;
int nsection = 0;
settings = gtk_settings_get_default();
g_object_get(settings, "gtk-theme-name", &theme_name, NULL);
prov = gtk_css_provider_get_named(theme_name, NULL);
if (prov != NULL)
css_data = gtk_css_provider_to_string(prov);
else
css_data = (char *)"";
pcss = css_data;
for (;;)
{
char_t csect[256];
section = i_get_next_section(css_data, &pcss, &nsection);
if (section != NULL)
{
uint32_t sect_n = 0;
str_copy_cn(csect, sizeof(csect), section, nsection);
csect[nsection] = '\0';
if (kCSS_ENTRY == NULL)
{
if (i_section(csect, "entry", §_n) == TRUE)
kCSS_ENTRY = str_cn(csect, sect_n);
}
if (kCSS_BUTTON == NULL)
{
if (i_section(csect, "button", §_n) == TRUE)
kCSS_BUTTON = str_cn(csect, sect_n);
}
if (kCSS_RADIO == NULL)
{
if (i_section(csect, "radiobutton", §_n) == TRUE)
kCSS_RADIO = str_cn(csect, sect_n);
else if (i_section(csect, "radio", §_n) == TRUE)
kCSS_RADIO = str_cn(csect, sect_n);
}
if (kCSS_CHECK == NULL)
{
if (i_section(csect, "checkbutton", §_n) == TRUE)
kCSS_CHECK = str_cn(csect, sect_n);
else if (i_section(csect, "check", §_n) == TRUE)
kCSS_CHECK = str_cn(csect, sect_n);
}
if (kCSS_COMBOBOX == NULL)
{
if (i_section(csect, "combobox", §_n) == TRUE)
kCSS_COMBOBOX = str_cn(csect, sect_n);
else if (i_section(csect, "GtkComboBox", §_n) == TRUE)
kCSS_COMBOBOX = str_cn(csect, sect_n);
}
if (kCSS_FRAME == NULL)
{
if (i_section(csect, "frame", §_n) == TRUE)
kCSS_FRAME = str_cn(csect, sect_n);
}
if (kCSS_TEXTVIEW == NULL)
{
if (i_section(csect, "textview", §_n) == TRUE)
kCSS_TEXTVIEW = str_cn(csect, sect_n);
else if (i_section(csect, "GtkTextView", §_n) == TRUE)
kCSS_TEXTVIEW = str_cn(csect, sect_n);
}
if (kCSS_TEXTVIEWTEXT == NULL)
{
if (i_section(csect, "textview text", §_n) == TRUE)
kCSS_TEXTVIEWTEXT = str_cn(csect, sect_n);
}
if (kCSS_PROGRESSBAR == NULL)
{
if (i_section(csect, "GtkProgressBar", §_n) == TRUE)
kCSS_PROGRESSBAR = str_cn(csect, sect_n);
else if (i_section(csect, "progressbar", §_n) == TRUE)
kCSS_PROGRESSBAR = str_cn(csect, sect_n);
}
if (kCSS_ENTRY == NULL || kCSS_BUTTON == NULL || kCSS_RADIO == NULL || kCSS_CHECK == NULL || kCSS_COMBOBOX == NULL || kCSS_FRAME == NULL || kCSS_TEXTVIEW == NULL || kCSS_TEXTVIEWTEXT == NULL || kCSS_PROGRESSBAR == NULL)
i_jump_next_section(&pcss);
else
break;
}
else
{
break;
}
}
if (kCSS_TEXTVIEW == NULL)
{
if (kCSS_TEXTVIEWTEXT != NULL)
{
const char_t *t = tc(kCSS_TEXTVIEWTEXT);
while (*t != 0)
{
if (*t == ' ')
{
kCSS_TEXTVIEW = str_cn(tc(kCSS_TEXTVIEWTEXT), (uint32_t)(t - tc(kCSS_TEXTVIEWTEXT)));
break;
}
t++;
}
}
}
if (kCSS_TEXTVIEW == NULL)
{
#if GTK_CHECK_VERSION(3, 22, 0)
kCSS_TEXTVIEW = str_c("textview");
#else
kCSS_TEXTVIEW = str_c("GtkTextView");
#endif
}
if (kCSS_TEXTVIEWTEXT == NULL && kCSS_TEXTVIEW != NULL)
kCSS_TEXTVIEWTEXT = str_copy(kCSS_TEXTVIEW);
if (kCSS_ENTRY == NULL)
log_printf("No kCSS_ENTRY found in css theme");
if (kCSS_BUTTON == NULL)
log_printf("No kCSS_BUTTON found in css theme");
if (kCSS_RADIO == NULL)
log_printf("No kCSS_RADIO found in css theme");
if (kCSS_CHECK == NULL)
log_printf("No kCSS_CHECK found in css theme");
if (kCSS_COMBOBOX == NULL)
log_printf("No kCSS_COMBOBOX found in css theme");
if (kCSS_FRAME == NULL)
log_printf("No kCSS_FRAME found in css theme");
if (kCSS_PROGRESSBAR == NULL)
log_printf("No kCSS_PROGRESSBAR found in css theme");
g_free(theme_name);
g_free(css_data);
g_object_unref(settings);
}
#if !defined(__ASSERTS__)
#if GLIB_CHECK_VERSION(2, 50, 0)
static GLogWriterOutput i_null_writter(GLogLevelFlags log_level, const GLogField *fields, gsize n_fields,
gpointer user_data)
{
unref(log_level);
unref(fields);
unref(n_fields);
unref(user_data);
return G_LOG_WRITER_HANDLED;
}
#else
static void i_null_writter(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
{
unref(log_domain);
unref(log_level);
unref(message);
unref(user_data);
}
#endif
#endif
void _osglobals_init(void)
{
#if !defined(__ASSERTS__)
#if GLIB_CHECK_VERSION(2, 50, 0)
g_log_set_writer_func(i_null_writter, NULL, NULL);
#else
g_log_set_default_handler(i_null_writter, NULL);
#endif
#endif
i_parse_gtk_theme();
i_impostor_window();
}
void _osglobals_finish(void)
{
if (kWINDOW != NULL)
{
g_object_ref_sink(kWINDOW);
g_object_unref(kWINDOW);
kWINDOW = NULL;
kLABEL = NULL;
kENTRY = NULL;
kBUTTON = NULL;
kSEPARATOR = NULL;
kLINKBUTTON = NULL;
kTABLE = NULL;
kHEADER = NULL;
}
if (kCHECKSBITMAP != NULL)
g_object_unref(kCHECKSBITMAP);
str_destopt(&kCSS_ENTRY);
str_destopt(&kCSS_BUTTON);
str_destopt(&kCSS_RADIO);
str_destopt(&kCSS_CHECK);
str_destopt(&kCSS_COMBOBOX);
str_destopt(&kCSS_FRAME);
str_destopt(&kCSS_TEXTVIEW);
str_destopt(&kCSS_TEXTVIEWTEXT);
str_destopt(&kCSS_PROGRESSBAR);
}
bool_t _osglobals_impostor_mapped(void)
{
return i_IMPOSTOR_MAPPED;
}
GtkStyleContext *_osglobals_entry_context(void)
{
cassert(kENTRY != NULL);
return gtk_widget_get_style_context(kENTRY);
}
GtkStyleContext *_osglobals_button_context(void)
{
cassert(kBUTTON != NULL);
return gtk_widget_get_style_context(kBUTTON);
}
GtkStyleContext *_osglobals_table_context(void)
{
cassert(kTABLE != NULL);
return gtk_widget_get_style_context(kTABLE);
}
const char_t *_osglobals_css_entry(void)
{
cassert(str_empty(kCSS_ENTRY) == FALSE);
return tc(kCSS_ENTRY);
}
const char_t *_osglobals_css_button(void)
{
cassert(str_empty(kCSS_BUTTON) == FALSE);
return tc(kCSS_BUTTON);
}
const char_t *_osglobals_css_radio(void)
{
cassert(str_empty(kCSS_RADIO) == FALSE);
return tc(kCSS_RADIO);
}
const char_t *_osglobals_css_check(void)
{
cassert(str_empty(kCSS_CHECK) == FALSE);
return tc(kCSS_CHECK);
}
const char_t *_osglobals_css_combobox(void)
{
cassert(str_empty(kCSS_COMBOBOX) == FALSE);
return tc(kCSS_COMBOBOX);
}
const char_t *_osglobals_css_textview(void)
{
return tc(kCSS_TEXTVIEW);
}
const char_t *_osglobals_css_textview_text(void)
{
return tc(kCSS_TEXTVIEWTEXT);
}
const char_t *_osglobals_css_progressbar(void)
{
return tc(kCSS_PROGRESSBAR);
}
color_t _osglobals_text_color(void)
{
cassert(kTEXT_COLOR != 0);
return kTEXT_COLOR;
}
color_t _osglobals_seltext_color(void)
{
cassert(kSELTX_COLOR != 0);
return kSELTX_COLOR;
}
color_t _osglobals_hottext_color(void)
{
cassert(kHOTTX_COLOR != 0);
return kHOTTX_COLOR;
}
color_t _osglobals_textbackdrop_color(void)
{
cassert(kTEXTBACKDROP_COLOR != 0);
return kTEXTBACKDROP_COLOR;
}
color_t _osglobals_seltextbackdrop_color(void)
{
cassert(kSELTXBACKDROP_COLOR != 0);
return kSELTXBACKDROP_COLOR;
}
color_t _osglobals_hottextbackdrop_color(void)
{
cassert(kHOTTXBACKDROP_COLOR != 0);
return kHOTTXBACKDROP_COLOR;
}
color_t _osglobals_border_color(void)
{
return kLINE_COLOR;
}
uint32_t _osglobals_check_width(void)
{
cassert(kCHECK_WIDTH != 0);
return kCHECK_WIDTH;
}
uint32_t _osglobals_check_height(void)
{
cassert(kCHECK_HEIGHT != 0);
return kCHECK_HEIGHT;
}
uint32_t _osglobals_entry_height(void)
{
cassert(kENTRY_HEIGHT != 0);
return kENTRY_HEIGHT;
}
uint32_t _osglobals_progress_height(void)
{
cassert(kPROGRESS_HEIGHT != 0);
return kPROGRESS_HEIGHT;
}
uint32_t _osglobals_scrollbar_height(void)
{
cassert(kSCROLLBAR_HEIGHT != 0);
return kSCROLLBAR_HEIGHT;
}
GdkPixbuf *_osglobals_checks_bitmap(void)
{
cassert(kCHECKSBITMAP != NULL);
return kCHECKSBITMAP;
}
static void i_write_rgb(Stream *stm, const char_t *prop, const color_t color)
{
uint8_t r, g, b;
color_get_rgb(color, &r, &g, &b);
stm_printf(stm, "%s: rgb(%d, %d, %d);", prop, r, g, b);
}
String *_osglobals_frame_focus_css(void)
{
Stream *stm = stm_memory(1024);
String *str = NULL;
stm_printf(stm, "%s > border {", tc(kCSS_FRAME));
i_write_rgb(stm, "border-left-color", kBORDFOCUS_COLOR);
i_write_rgb(stm, "border-right-color", kBORDFOCUS_COLOR);
i_write_rgb(stm, "border-top-color", kBORDFOCUS_COLOR);
i_write_rgb(stm, "border-bottom-color", kBORDFOCUS_COLOR);
stm_printf(stm, "} %s {", tc(kCSS_FRAME));
i_write_rgb(stm, "border-left-color", kBORDFOCUS_COLOR);
i_write_rgb(stm, "border-right-color", kBORDFOCUS_COLOR);
i_write_rgb(stm, "border-top-color", kBORDFOCUS_COLOR);
i_write_rgb(stm, "border-bottom-color", kBORDFOCUS_COLOR);
stm_printf(stm, "}");
str = stm_str(stm);
stm_close(&stm);
return str;
}
void _osglobals_restore_focus(GtkWidget *window, GtkWidget *widget)
{
kRESTORE_FOCUS_WINDOW = window;
kRESTORE_FOCUS_WIDGET = widget;
}