From be797b01c9ecc63cd645f8a7d56d91569eb803e7 Mon Sep 17 00:00:00 2001 From: Eric Wertz Date: Mon, 6 Nov 2017 10:19:19 -0500 Subject: [PATCH 1/1] first commit --- Makefile | 11 ++ eric_window.c | 126 +++++++++++++++ eric_window.h | 35 ++++ main.c | 437 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 609 insertions(+) create mode 100644 Makefile create mode 100644 eric_window.c create mode 100644 eric_window.h create mode 100755 main.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1044021 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CC = gcc +CFLAGS = -Wunused-result -g -Wall -lm -lX11 -lXext `pkg-config --cflags gtk+-3.0` +LDFLAGS = `pkg-config --libs gtk+-3.0` +DEPS = eric_window.h +OBJ = eric_window.o main.o + +%.o: %.c $(DEPS) + $(CC) $(CFLAGS) -c -o $@ $< + +ericlaunch: $(OBJ) + gcc $(LDFLAGS) $(CFLAGS) -o $@ $^ diff --git a/eric_window.c b/eric_window.c new file mode 100644 index 0000000..59cbe66 --- /dev/null +++ b/eric_window.c @@ -0,0 +1,126 @@ +#include "eric_window.h" + +double gdk_rgba_get_luminance( GdkRGBA *color ) +{ + return ( color->red ) * 0.2126 + + ( color->green ) * 0.7152 + + ( color->blue ) * 0.0722; +} + +void gdk_color_lerp( GdkRGBA* c1, GdkRGBA* c2, double s, GdkRGBA* out ) +{ + out->red = c1->red + ( c2->red - c1->red ) * s; + out->green = c1->green + ( c2->green - c1->green ) * s; + out->blue = c1->blue + ( c2->blue - c1->blue ) * s; + out->alpha = c1->alpha + ( c2->alpha - c1->alpha ) * s; +} + +gboolean eric_window_animation_timer( eric_window* w ) +{ + w->background_change_percentage += 0.05; + gdk_color_lerp( &w->background_color_old, &w->background_color_new, + w->background_change_percentage, &w->background_color); + gdk_color_lerp( &w->text_color_old, &w->text_color_new, + w->background_change_percentage, &w->text_color); + gtk_widget_queue_draw( w->window ); + + if( w->background_change_percentage >= 1.0 ) return FALSE; + + return TRUE; +} + +gboolean eric_window_draw( GtkWidget* widget, cairo_t* cr, eric_window* w ) +{ + cairo_set_operator(cr,CAIRO_OPERATOR_SOURCE); + w->background_color.alpha = 0.75; + gdk_cairo_set_source_rgba( cr, &w->background_color ); + cairo_paint( cr ); + + if( w->draw_callback == NULL ) + return FALSE; + else + return w->draw_callback( widget, cr, w ); +} + + +void eric_window_screen_changed( GtkWidget *widget, GdkScreen *old_screen, gpointer userdata ) +{ + GdkVisual *visual; + + GdkScreen* screen=gtk_widget_get_screen(widget); + if(!screen) return; + + visual = gdk_screen_get_rgba_visual(screen); + if(visual==NULL) visual=gdk_screen_get_system_visual(screen); + + gtk_widget_set_visual(widget,visual); +} + +void eric_window_gsettings_value_changed( GSettings *settings, const gchar *key, eric_window* w ) +{ + if( strcmp( key, "primary-color" ) == 0 ) + { + w->background_color_old = w->background_color; + gdk_rgba_parse( &w->background_color_new, g_settings_get_string( settings, "primary-color" ) ); + w->text_color_old = w->text_color; + if( gdk_rgba_get_luminance( &w->background_color_new ) > 0.5 ) + gdk_rgba_parse( &w->text_color_new, "#000000" ); + else + gdk_rgba_parse( &w->text_color_new, "#FFFFFF" ); + + w->background_change_percentage = 0.0; + g_timeout_add( 32, (gpointer)eric_window_animation_timer, w ); + } +} + +eric_window* eric_window_create( int width, int height, char* title ) +{ + eric_window* w = malloc( sizeof( eric_window ) ); + w->draw_callback = NULL; + + if( title == NULL ) + { + title = "eric window"; + } + + w->window = gtk_window_new( GTK_WINDOW_TOPLEVEL ); + gtk_window_set_title( GTK_WINDOW( w->window ), title ); + gtk_window_resize( GTK_WINDOW( w->window ), width, height ); + gtk_widget_add_events( w->window, GDK_STRUCTURE_MASK ); + + gtk_widget_set_app_paintable( w->window, TRUE ); + + g_signal_connect( G_OBJECT( w->window ), "draw", G_CALLBACK(eric_window_draw), (gpointer)w ); + g_signal_connect( G_OBJECT( w->window ), "screen-changed", G_CALLBACK(eric_window_screen_changed), (gpointer)w ); + g_signal_connect( G_OBJECT( w->window ), "delete-event", gtk_main_quit, NULL ); + + eric_window_screen_changed( w->window, NULL, NULL ); + + /* GSettings Stuff */ + GSettings* gsettings; + GSettingsSchema* gsettings_schema; + + gsettings_schema = g_settings_schema_source_lookup( g_settings_schema_source_get_default(), + "org.gnome.desktop.background", + TRUE ); + if( gsettings_schema ) + { + g_settings_schema_unref (gsettings_schema); + gsettings_schema = NULL; + gsettings = g_settings_new ( "org.gnome.desktop.background" ); + } + + g_signal_connect_data( gsettings, "changed", G_CALLBACK( eric_window_gsettings_value_changed ), (gpointer)w, 0, 0 ); + gdk_rgba_parse( &w->background_color, g_settings_get_string( gsettings, "primary-color" ) ); + if( gdk_rgba_get_luminance( &w->background_color ) > 0.5 ) + gdk_rgba_parse( &w->text_color, "#000000" ); + else + gdk_rgba_parse( &w->text_color, "#FFFFFF" ); + + gsettings = g_settings_new ( "org.gnome.desktop.interface" ); + w->interface_scale = (double)g_settings_get_uint( gsettings, "scaling-factor" ); + if( w->interface_scale < 1.0 ) + w->interface_scale = 1.0; + + return w; +} diff --git a/eric_window.h b/eric_window.h new file mode 100644 index 0000000..0a794ed --- /dev/null +++ b/eric_window.h @@ -0,0 +1,35 @@ +#pragma once +/* + * eric_window.h + * creates a transluscent window using the current theme color + */ + +#include +#include +#include + +#include +#include + +typedef struct eric_window eric_window; +struct eric_window +{ + GtkWidget* window; + GdkRGBA background_color; + GdkRGBA background_color_old; + GdkRGBA background_color_new; + GdkRGBA text_color; + GdkRGBA text_color_old; + GdkRGBA text_color_new; + double background_change_percentage; + gboolean (*draw_callback)( GtkWidget* widget, cairo_t* cr, eric_window* w ); + double interface_scale; +}; + +double gdk_rgba_get_luminance( GdkRGBA *color ); +void gdk_color_lerp( GdkRGBA* c1, GdkRGBA* c2, double s, GdkRGBA* out ); +gboolean eric_window_animation_timer( eric_window* w ); +gboolean eric_window_draw( GtkWidget* widget, cairo_t* cr, eric_window* w ); +void eric_window_screen_changed( GtkWidget *widget, GdkScreen *old_screen, gpointer userdata ); +void eric_window_gsettings_value_changed( GSettings *settings, const gchar *key, eric_window* w ); +eric_window* eric_window_create( int width, int height, char* title ); diff --git a/main.c b/main.c new file mode 100755 index 0000000..20b5fd2 --- /dev/null +++ b/main.c @@ -0,0 +1,437 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include + +PangoContext* pango_context; + +#include "eric_window.h" + +#define SCALE_VALUE(x) (x)*window->interface_scale + +int ICON_SIZE = 128; + +double WINDOW_WIDTH=640; +double WINDOW_HEIGHT=480; + +typedef struct +{ + PangoLayout* layout; + int layout_width; + char name[64]; + double x; + double y; + GdkPixbuf* pixbuf; + char launch_command[128]; + gboolean hover; +} LAUNCHER_APP_ICON; + +eric_window* window; +GtkWidget* entry; + +GList* icon_list; + +gboolean windowed; + +GdkPixbuf* get_app_icon(const char* name,int size) +{ + GtkIconTheme* theme=gtk_icon_theme_get_default(); + return gtk_icon_theme_load_icon(theme,name,size,0,NULL); +} + +void draw_rounded_rect(cairo_t* cr,double x,double y,double w,double h,double r) +{ + cairo_move_to(cr,x+r,y); + cairo_line_to(cr,x+w-r*2,y); + cairo_arc(cr,x+w-r,y+r,r,-M_PI/2.0,0); + cairo_line_to(cr,x+w,y+h-r*2); + cairo_arc(cr,x+w-r,y+h-r,r,0,M_PI/2.0); + cairo_line_to(cr, x+r,y+h); + cairo_arc(cr,x+r,y+h-r,r,M_PI/2.0,M_PI); + cairo_line_to(cr, x, y+r); + cairo_arc(cr,x+r,y+r,r,M_PI,-M_PI/2.0); + cairo_close_path(cr); +} + +void draw_app_icon(cairo_t* cr,LAUNCHER_APP_ICON* icon) +{ + double icon_center=(double)ICON_SIZE/2.0; + double x=icon->x; + double y=icon->y; + PangoRectangle rect; + + //Hover rounded rect highlight + if(icon->hover) + { + cairo_set_source_rgba(cr,( window->text_color.red ), + ( window->text_color.green ), + ( window->text_color.blue ), + 0.25); + draw_rounded_rect(cr,icon->x-ICON_SIZE/8,icon->y-ICON_SIZE/8,ICON_SIZE*1.25,ICON_SIZE*1.25,8); + cairo_fill(cr); + } + + //Draw Icon + gdk_cairo_set_source_pixbuf(cr,icon->pixbuf,x+ICON_SIZE/4,y+ICON_SIZE/8); + cairo_paint(cr); + + pango_layout_get_pixel_extents( icon->layout, NULL, &rect ); + + double text_x= x + icon_center - ((double)rect.width / 2.0); + double text_y=y+ICON_SIZE*0.75; + + //Draw a shadow + cairo_set_source_rgba(cr,( 1.0 - window->text_color.red ), + ( 1.0 - window->text_color.green ), + ( 1.0 - window->text_color.blue ), + 0.25); + cairo_move_to(cr,text_x - rect.x + SCALE_VALUE(1.0),text_y + SCALE_VALUE(1.0)); + pango_cairo_layout_path( cr, icon->layout ); + cairo_fill(cr); + + gdk_cairo_set_source_rgba( cr, &window->text_color ); + cairo_move_to(cr,text_x - rect.x,text_y); + pango_cairo_layout_path( cr, icon->layout ); + + cairo_fill(cr); +} + +GFunc draw_icon_list_item( gpointer data, gpointer user ) +{ + draw_app_icon((cairo_t*)user,(LAUNCHER_APP_ICON*)data); +} + +static gboolean draw( GtkWidget* widget, cairo_t* cr, eric_window* w ) +{ + cairo_set_operator(cr,CAIRO_OPERATOR_OVER); + + g_list_foreach(icon_list,(GFunc)draw_icon_list_item,cr); + + return FALSE; +} + +static gboolean draw_entry( GtkWidget* widget, cairo_t* cr, gpointer user_data ) +{ + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 0.75); + + cairo_paint(cr); + + return FALSE; +} + +double icon_layout_x; +double icon_layout_y; +double icon_layout_max_x; +double icon_layout_margin; +double icon_layout_stride_x; +double icon_layout_stride_y; +double icon_layout_width; +double icon_width; + +GFunc layout_app_icon(gpointer data,gpointer user) +{ + LAUNCHER_APP_ICON* icon=(LAUNCHER_APP_ICON*)data; + icon->x=icon_layout_x; + icon->y=icon_layout_y; + + pango_layout_set_alignment( icon->layout, PANGO_ALIGN_CENTER ); + pango_layout_set_width( icon->layout, (int)(ICON_SIZE*0.99) * PANGO_SCALE ); + pango_layout_set_text( icon->layout, icon->name, strlen(icon->name) ); + pango_layout_get_pixel_size( icon->layout, &icon->layout_width, NULL ); + + icon_layout_x+=icon_layout_stride_x; + if(icon_layout_x>icon_layout_max_x) + { + icon_layout_x=icon_layout_margin; + icon_layout_y+=icon_layout_stride_y; + } + + return 0; +} + +//This is a really horrible naming convention change it +void do_icon_layout() +{ + icon_layout_x=ICON_SIZE/2; + icon_layout_y=ICON_SIZE*1.5; + + icon_layout_margin=ICON_SIZE/2; + icon_layout_width = WINDOW_WIDTH - ICON_SIZE; + icon_layout_max_x=icon_layout_margin+(icon_layout_width-ICON_SIZE*1.25); + + double icon_count=(icon_layout_width-ICON_SIZE*1.25)/(ICON_SIZE*1.25); + icon_width = icon_layout_width / floor(icon_count); + icon_layout_stride_x=(icon_layout_width-ICON_SIZE*1.25)/floor(icon_count); + icon_layout_stride_y=ICON_SIZE*1.5; + + icon_layout_x=icon_layout_margin; + icon_layout_y=ICON_SIZE*1.5; + + g_list_foreach(icon_list,(GFunc)layout_app_icon,NULL); +} + +void add_app_icon(const char* name,const char* icon_name,const char* cmd) +{ + LAUNCHER_APP_ICON* icon=malloc(sizeof(LAUNCHER_APP_ICON)); + strcpy(icon->name,name); + icon->pixbuf=get_app_icon(icon_name,ICON_SIZE*0.5); + icon->x=icon_layout_x; + icon->y=icon_layout_y; + strcpy(icon->launch_command,cmd); + + icon->layout = pango_layout_new( pango_context ); + pango_layout_set_wrap (icon->layout, PANGO_WRAP_WORD ); + pango_layout_set_auto_dir (icon->layout, FALSE); + //pango_layout_set_width (icon->layout, -1); + pango_layout_set_text (icon->layout, name, strlen (name)); + pango_layout_get_pixel_size (icon->layout, &icon->layout_width, NULL); + + icon_list=g_list_append(icon_list,icon); + + icon_layout_x+=ICON_SIZE*2; + if(icon_layout_x>WINDOW_WIDTH) + { + icon_layout_x=ICON_SIZE/2; + icon_layout_y+=ICON_SIZE*2; + } + icon->hover=FALSE; +} + +gboolean changedstate; + +GFunc icon_check_hover(gpointer data,gpointer user) +{ + LAUNCHER_APP_ICON* icon=(LAUNCHER_APP_ICON*)data; + GdkEventMotion* ev=(GdkEventMotion*)user; + + if(ev->x > icon->x && ev->x < icon->x+ICON_SIZE && + ev->y > icon->y && ev->y < icon->y+ICON_SIZE) + { + if(icon->hover==FALSE) changedstate=TRUE; + icon->hover=TRUE; + } + else + { + if(icon->hover==TRUE) changedstate=TRUE; + icon->hover=FALSE; + } + + return 0; +} + +gboolean window_mouse_move(GtkWidget* widget,GdkEvent* event,gpointer user) +{ + changedstate=FALSE; + + g_list_foreach(icon_list,(GFunc)icon_check_hover,(gpointer)&event->motion); + + if( changedstate ) + gtk_widget_queue_draw( window->window ); + + return FALSE; +} + +gboolean window_key_press(GtkWidget* widget,GdkEvent* event,gpointer user) +{ + GdkEventKey* keyevent=&event->key; + if(keyevent->keyval==GDK_KEY_Escape) gtk_main_quit(); + + if(keyevent->keyval==GDK_KEY_Return) + { + char buffer[512]; + GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask(); + + if( (keyevent->state & modifiers) == GDK_SHIFT_MASK ) + sprintf(buffer,"xfce4-terminal -e \"%s\" &",gtk_entry_get_text(GTK_ENTRY(entry))); + else + sprintf(buffer,"%s &",gtk_entry_get_text(GTK_ENTRY(entry))); + + + if(system(buffer)!=-1) gtk_main_quit(); + } + + return FALSE; +} + +GFunc icon_test_launch(gpointer data,gpointer user) +{ + LAUNCHER_APP_ICON* icon=(LAUNCHER_APP_ICON*)data; + GdkEventButton* ev=(GdkEventButton*)user; + + if(ev->x > icon->x && ev->x < icon->x+ICON_SIZE && + ev->y > icon->y && ev->y < icon->y+ICON_SIZE) + { + char buffer[129]; + sprintf(buffer,"%s &",icon->launch_command); + system(buffer); + gtk_main_quit(); + } +} + + +gboolean window_button_release(GtkWidget* widget,GdkEvent* event,gpointer user) +{ + GdkEventButton* buttonevent=&event->button; + + g_list_foreach(icon_list,(GFunc)icon_test_launch,(gpointer)buttonevent); + + return TRUE; +} + +void parse_desktop_entry(const char* name) +{ + char buffer[256]; + GKeyFile* key_file = g_key_file_new(); + + sprintf( buffer, "/usr/share/applications/%s.desktop", name ); + printf( "%s\n", buffer ); + if( !g_key_file_load_from_file( key_file, buffer, G_KEY_FILE_NONE, NULL ) ) + { + return; + } + + gchar* app_name = g_key_file_get_string( key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL ); + gchar* icon_name = g_key_file_get_string( key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL ); + gchar* exec = g_key_file_get_string( key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL ); + + + printf( "%s, %s, %s\n", app_name, icon_name, exec ); + add_app_icon(app_name,icon_name,exec); +} + +void do_layout_fullscreen() +{ + GdkScreen* screen=gdk_screen_get_default(); + + GdkRectangle mon_geom; + gdk_screen_get_monitor_geometry( screen, gdk_screen_get_primary_monitor(screen), &mon_geom ); + + WINDOW_WIDTH=mon_geom.width; + WINDOW_HEIGHT=mon_geom.height; +} + +void do_layout_windowed(int width,int height) +{ + WINDOW_WIDTH=width; + WINDOW_HEIGHT=height; +} + +gboolean entry_lose_focus(GtkWidget* widget,GdkEvent* event, gpointer user) +{ + gtk_main_quit(); + return TRUE; +} + +//Command line arguments +// -w is windowed mode +// -p X Y is windowed mode position +// -d W H is windowed mode width/height +// -s X is icon size + +int main(int argc, char **argv) +{ + /* boilerplate initialization code */ + gtk_init(&argc, &argv); + + windowed=FALSE; + int x=0; + int y=0; + int w=640; + int h=480; + char fontdesc[256]; + + //read CLI arguments + int i; + for(i=0;iwindow, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK ); + g_signal_connect( G_OBJECT( window->window ), "key-press-event", G_CALLBACK(window_key_press), NULL); + g_signal_connect( G_OBJECT( window->window ), "motion-notify-event", G_CALLBACK(window_mouse_move), NULL); + g_signal_connect( G_OBJECT( window->window ), "button-release-event", G_CALLBACK(window_button_release), NULL); + gtk_widget_set_size_request(window->window,WINDOW_WIDTH,WINDOW_HEIGHT); + + ICON_SIZE = SCALE_VALUE(ICON_SIZE); + + window->draw_callback = draw; + + pango_context = gtk_widget_create_pango_context( window->window ); + sprintf( fontdesc, "Source Sans Pro Regular %ipx", MAX( 12, ICON_SIZE / 8 ) ); + PangoFontDescription* font=pango_font_description_from_string( fontdesc ); + pango_context_set_font_description( pango_context, font ); + + icon_list=NULL; + //TODO: load custom file here... + parse_desktop_entry( "chromium" ); + parse_desktop_entry( "xfce4-terminal" ); + parse_desktop_entry( "libreoffice-writer" ); + parse_desktop_entry( "libreoffice-calc" ); + parse_desktop_entry( "Thunar" ); + parse_desktop_entry( "steam" ); + parse_desktop_entry( "xfce-settings-manager" ); + parse_desktop_entry( "gnome-system-monitor" ); + + do_icon_layout(); + + gtk_window_set_icon( GTK_WINDOW( window->window ), NULL ); + gtk_window_set_type_hint(GTK_WINDOW( window->window ), GDK_WINDOW_TYPE_HINT_DIALOG ); + gtk_window_set_decorated(GTK_WINDOW( window->window ), FALSE ); + gtk_window_set_resizable(GTK_WINDOW( window->window ), FALSE ); + + + //Layout widgets + GtkWidget* fixed=gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(window->window),fixed); + entry=gtk_entry_new(); + gtk_fixed_put(GTK_FIXED(fixed),entry,ICON_SIZE/4,ICON_SIZE/4); + gtk_widget_set_size_request(entry,WINDOW_WIDTH-ICON_SIZE/2,ICON_SIZE); + //gtk_widget_set_app_paintable(entry, TRUE); + g_signal_connect(G_OBJECT(entry), "draw", G_CALLBACK(draw_entry), NULL); + g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(entry_lose_focus), NULL); + + //Set entry font here + font=pango_font_description_from_string("Source Sans Pro Regular 24"); + gtk_widget_override_font(entry,font); + pango_font_description_free(font); + + gtk_widget_show_all( window->window ); + + gtk_window_move( GTK_WINDOW( window->window ), x, y ); + + gtk_main(); + + return 0; +} -- 2.47.0