mirror of
https://github.com/Alcaro/Flips.git
synced 2026-04-17 15:25:59 -05:00
384 lines
11 KiB
C++
384 lines
11 KiB
C++
#include "window.h"
|
|
//#include "../image.h"
|
|
//#include "minir.h"
|
|
#include "../file.h"
|
|
#include "../os.h"
|
|
#ifdef ARGUI_GTK3
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <gtk/gtk.h>
|
|
#ifdef ARGUIPROT_X11
|
|
#include <gdk/gdkx.h>
|
|
#else
|
|
#error Only X11 supported.
|
|
#endif
|
|
|
|
//Number of ugly hacks: 8
|
|
//The status bar is a GtkGrid with GtkLabel, not a GtkStatusbar, because I couldn't get GtkStatusbar
|
|
// to cooperate. While the status bar is a GtkBox, I couldn't find how to get rid of its child.
|
|
//The status bar manages layout manually (by resizing the GtkLabels), because a GtkGrid with a large
|
|
// number of slots seem to assign pixels 3,3,2,2 if told to split 10 pixels to 4 equally sized
|
|
// slots, as opposed to 2,3,2,3 or 3,2,3,2.
|
|
//Label widgets that ellipsize, as well as status bar labels, are declared with max width 1, and
|
|
// then ellipsizes. Apparently they use more space than maximum if they can. This doesn't seem to be
|
|
// documented, and is therefore not guaranteed to continue working.
|
|
//gtk_main_iteration_do(false) is called twice, so GTK thinks we're idle and sends out the mouse
|
|
// move events. Most of the time is spent waiting for A/V drivers, and our mouse move processor is
|
|
// cheap. (Likely fixable in GTK+ 3.12, but I'm on 3.8.)
|
|
//Refreshing a listbox is done by telling it to redraw, not by telling it that contents have changed.
|
|
// It's either that or send tens of thousands of contents-changed events, and I'd rather not.
|
|
//gtk_widget_override_background_color does nothing to GtkTextEntry, due to a background-image
|
|
// gradient. I had to throw a bit of their fancy CSS at it.
|
|
//GtkTreeView has non-constant performance for creating a pile of rows, and gets terrible around
|
|
// 100000. I had to cap it to 65536.
|
|
//The size of GtkTreeView is complete nonsense. I haven't found how to get it to give out its real
|
|
// row height, nor could I figure out what the nonsense I get from the tell-me-your-height functions
|
|
// is, but I am sure that whatever tricks I will need to pull fits here.
|
|
|
|
//static GdkFilterReturn scanfilter(GdkXEvent* xevent, GdkEvent* event, gpointer data)
|
|
//{
|
|
// XEvent* ev=(XEvent*)xevent;
|
|
// if (ev->type==Expose) printf("ex=%lu\n", ev->xexpose.window);
|
|
// return GDK_FILTER_CONTINUE;
|
|
//}
|
|
|
|
//#include<sys/resource.h>
|
|
|
|
#ifdef ARGUIPROT_X11
|
|
struct window_x11_info window_x11;
|
|
#endif
|
|
|
|
static bool window_init(bool require, int * argc, char * * argv[])
|
|
{
|
|
//struct rlimit core_limits;core_limits.rlim_cur=core_limits.rlim_max=64*1024*1024;setrlimit(RLIMIT_CORE,&core_limits);
|
|
#ifdef DEBUG
|
|
g_log_set_always_fatal((GLogLevelFlags)(G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING));
|
|
#endif
|
|
#ifdef ARGUIPROT_X11
|
|
XInitThreads();
|
|
#endif
|
|
if (require)
|
|
{
|
|
gtk_init(argc, argv);
|
|
}
|
|
else
|
|
{
|
|
if (!gtk_init_check(argc, argv)) return false;
|
|
}
|
|
//gdk_window_add_filter(NULL,scanfilter,NULL);
|
|
//#ifndef NO_ICON
|
|
// struct image img;
|
|
// png_decode(icon_minir_64x64_png,sizeof(icon_minir_64x64_png), &img, fmt_argb8888);
|
|
// //we could tell it how to free this, but it will be used until replaced, and it won't be replaced.
|
|
// gtk_window_set_default_icon(gdk_pixbuf_new_from_data((guchar*)img.pixels, GDK_COLORSPACE_RGB, true, 8, 64,64, 64*4, NULL, NULL));
|
|
//#endif
|
|
#ifdef ARGUIPROT_X11
|
|
window_x11.display=gdk_x11_get_default_xdisplay();
|
|
window_x11.screen=gdk_x11_get_default_screen();
|
|
window_x11.root=gdk_x11_get_default_root_xwindow();//alternatively XRootWindow(window_x11.display, window_x11.screen)
|
|
#endif
|
|
errno=0;
|
|
return true;
|
|
}
|
|
|
|
void window_init(int * argc, char * * argv[])
|
|
{
|
|
window_init(true, argc, argv);
|
|
}
|
|
|
|
bool window_try_init(int * argc, char * * argv[])
|
|
{
|
|
return window_init(false, argc, argv);
|
|
}
|
|
|
|
bool window_console_avail()
|
|
{
|
|
return getenv("TERM");
|
|
}
|
|
|
|
bool window_console_attach()
|
|
{
|
|
//nothing to do
|
|
return window_console_avail();
|
|
}
|
|
|
|
//file* file::create(const char * filename)
|
|
//{
|
|
// //TODO
|
|
// return create_fs(filename);
|
|
//}
|
|
|
|
//static void * mem_from_g_alloc(void * mem, size_t size)
|
|
//{
|
|
// if (g_mem_is_system_malloc()) return mem;
|
|
//
|
|
// if (!size) size=strlen((char*)mem)+1;
|
|
//
|
|
// void * ret=malloc(size);
|
|
// memcpy(ret, mem, size);
|
|
// g_free(ret);
|
|
// return ret;
|
|
//}
|
|
//
|
|
////enum mbox_sev { mb_info, mb_warn, mb_err };
|
|
////enum mbox_btns { mb_ok, mb_okcancel, mb_yesno };
|
|
//bool window_message_box(const char * text, const char * title, enum mbox_sev severity, enum mbox_btns buttons)
|
|
//{
|
|
// //"Please note that GTK_BUTTONS_OK, GTK_BUTTONS_YES_NO and GTK_BUTTONS_OK_CANCEL are discouraged by the GNOME HIG."
|
|
// //I do not listen to advise without a rationale. Tell me which section it violates, and I'll consider it.
|
|
// GtkMessageType sev[3]={ GTK_MESSAGE_OTHER, GTK_MESSAGE_WARNING, GTK_MESSAGE_ERROR };
|
|
// GtkButtonsType btns[3]={ GTK_BUTTONS_OK, GTK_BUTTONS_OK_CANCEL, GTK_BUTTONS_YES_NO };
|
|
// GtkWidget* dialog=gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, sev[severity], btns[buttons], "%s", text);
|
|
// gint ret=gtk_dialog_run(GTK_DIALOG(dialog));
|
|
// gtk_widget_destroy(dialog);
|
|
// return (ret==GTK_RESPONSE_ACCEPT || ret==GTK_RESPONSE_OK || ret==GTK_RESPONSE_YES);
|
|
//}
|
|
//
|
|
//const char * const * window_file_picker(window * parent,
|
|
// const char * title,
|
|
// const char * const * extensions,
|
|
// const char * extdescription,
|
|
// bool dylib,
|
|
// bool multiple)
|
|
//{
|
|
// static char * * ret=NULL;
|
|
// if (ret)
|
|
// {
|
|
// char * * del=ret;
|
|
// while (*del)
|
|
// {
|
|
// free(*del);
|
|
// del++;
|
|
// }
|
|
// free(ret);
|
|
// ret=NULL;
|
|
// }
|
|
//
|
|
// GtkFileChooser* dialog=GTK_FILE_CHOOSER(
|
|
// gtk_file_chooser_dialog_new(
|
|
// title,
|
|
// GTK_WINDOW(parent?(void*)parent->_get_handle():NULL),
|
|
// GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
// "_Cancel",
|
|
// GTK_RESPONSE_CANCEL,
|
|
// "_Open",
|
|
// GTK_RESPONSE_ACCEPT,
|
|
// NULL));
|
|
// gtk_file_chooser_set_select_multiple(dialog, multiple);
|
|
// gtk_file_chooser_set_local_only(dialog, dylib);
|
|
//
|
|
// GtkFileFilter* filter;
|
|
//
|
|
// if (*extensions)
|
|
// {
|
|
// filter=gtk_file_filter_new();
|
|
// gtk_file_filter_set_name(filter, extdescription);
|
|
// char extstr[64];
|
|
// extstr[0]='*';
|
|
// extstr[1]='.';
|
|
// while (*extensions)
|
|
// {
|
|
// strcpy(extstr+2, *extensions+(**extensions=='.'));
|
|
// gtk_file_filter_add_pattern(filter, extstr);
|
|
// extensions++;
|
|
// }
|
|
// gtk_file_chooser_add_filter(dialog, filter);
|
|
// }
|
|
//
|
|
// filter=gtk_file_filter_new();
|
|
// gtk_file_filter_set_name(filter, "All files");
|
|
// gtk_file_filter_add_pattern(filter, "*");
|
|
// gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
|
|
//
|
|
// if (gtk_dialog_run(GTK_DIALOG(dialog))!=GTK_RESPONSE_ACCEPT)
|
|
// {
|
|
// gtk_widget_destroy(GTK_WIDGET(dialog));
|
|
// return NULL;
|
|
// }
|
|
//
|
|
// GSList * list=gtk_file_chooser_get_uris(dialog);
|
|
// gtk_widget_destroy(GTK_WIDGET(dialog));
|
|
// unsigned int listlen=g_slist_length(list);
|
|
// if (!listlen)
|
|
// {
|
|
// g_slist_free(list);
|
|
// return NULL;
|
|
// }
|
|
// ret=malloc(sizeof(char*)*(listlen+1));
|
|
//
|
|
// char * * retcopy=ret;
|
|
// GSList * listcopy=list;
|
|
// while (listcopy)
|
|
// {
|
|
// *retcopy=window_get_absolute_path(NULL, (char*)listcopy->data, true);
|
|
// g_free(listcopy->data);
|
|
// retcopy++;
|
|
// listcopy=listcopy->next;
|
|
// }
|
|
// ret[listlen]=NULL;
|
|
// g_slist_free(list);
|
|
// return (const char * const *)ret;
|
|
//}
|
|
|
|
void window_run_iter()
|
|
{
|
|
gtk_main_iteration_do(false);
|
|
|
|
//Workaround for GTK thinking we're processing events slower than they come in. We're busy waiting
|
|
// for the AV drivers, and waiting less costs us nothing.
|
|
gtk_main_iteration_do(false);
|
|
}
|
|
|
|
void window_run_wait()
|
|
{
|
|
gtk_main_iteration_do(true);
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
char * window_get_absolute_path(const char * basepath, const char * path, bool allow_up)
|
|
{
|
|
if (!path) return NULL;
|
|
|
|
GFile* file;
|
|
const char * pathend = NULL; // gcc bug: this initialization does nothing, but gcc whines
|
|
if (basepath)
|
|
{
|
|
pathend = strrchr(basepath, '/');
|
|
gchar * basepath_dir = g_strndup(basepath, pathend+1-basepath);
|
|
file = g_file_new_for_commandline_arg_and_cwd(path, basepath_dir);
|
|
g_free(basepath_dir);
|
|
}
|
|
else
|
|
{
|
|
if (!allow_up) return NULL;
|
|
//not sure if gvfs URIs are absolute or not, so if absolute, let's use the one that works for both
|
|
//if not absolute, demand it's an URI
|
|
//per glib/gconvert.c g_filename_from_uri, file:// URIs are absolute
|
|
if (g_path_is_absolute(path)) file = g_file_new_for_commandline_arg(path);
|
|
else file = g_file_new_for_uri(path);
|
|
}
|
|
|
|
gchar * ret;
|
|
if (g_file_is_native(file)) ret = g_file_get_path(file);
|
|
else ret = g_file_get_uri(file);
|
|
g_object_unref(file);
|
|
|
|
if (!ret) return NULL;
|
|
|
|
if (!allow_up && strncmp(basepath, ret, pathend+1-basepath) != 0)
|
|
{
|
|
g_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
return (char*)mem_from_g_alloc(ret, 0);
|
|
}
|
|
|
|
char * window_get_native_path(const char * path)
|
|
{
|
|
if (!path) return NULL;
|
|
GFile* file=g_file_new_for_commandline_arg(path);
|
|
gchar * ret=g_file_get_path(file);
|
|
g_object_unref(file);
|
|
if (!ret) return NULL;
|
|
return (char*)mem_from_g_alloc(ret, 0);
|
|
}
|
|
|
|
|
|
|
|
bool file_read(const char * filename, void* * data, size_t * len)
|
|
{
|
|
if (!filename) return false;
|
|
GFile* file=g_file_new_for_commandline_arg(filename);
|
|
if (!file) return false;
|
|
|
|
char* ret;
|
|
gsize glen;
|
|
if (!g_file_load_contents(file, NULL, &ret, &glen, NULL, NULL))
|
|
{
|
|
g_object_unref(file);
|
|
return false;
|
|
}
|
|
g_object_unref(file);
|
|
if (len) *len=glen;
|
|
*data=mem_from_g_alloc(ret, glen);
|
|
return true;
|
|
}
|
|
|
|
bool file_write(const char * filename, const anyptr data, size_t len)
|
|
{
|
|
if (!filename) return false;
|
|
if (!len) return true;
|
|
GFile* file=g_file_new_for_commandline_arg(filename);
|
|
if (!file) return false;
|
|
bool success=g_file_replace_contents(file, data, len, NULL, false, G_FILE_CREATE_NONE, NULL, NULL, NULL);
|
|
g_object_unref(file);
|
|
return success;
|
|
}
|
|
|
|
bool file_read_to(const char * filename, anyptr data, size_t len)
|
|
{
|
|
if (!filename) return false;
|
|
if (!len) return true;
|
|
GFile* file=g_file_new_for_commandline_arg(filename);
|
|
if (!file) return false;
|
|
GFileInputStream* io=g_file_read(file, NULL, NULL);
|
|
if (!io)
|
|
{
|
|
g_object_unref(file);
|
|
return false;
|
|
}
|
|
GFileInfo* info=g_file_input_stream_query_info(io, G_FILE_ATTRIBUTE_STANDARD_SIZE, NULL, NULL);
|
|
gsize size=g_file_info_get_size(info);
|
|
if (size!=len) return false;
|
|
gsize actualsize;
|
|
bool success=g_input_stream_read_all(G_INPUT_STREAM(io), data, size, &actualsize, NULL, NULL);
|
|
g_input_stream_close(G_INPUT_STREAM(io), NULL, NULL);
|
|
g_object_unref(file);
|
|
g_object_unref(io);
|
|
g_object_unref(info);
|
|
if (!success || size!=actualsize)
|
|
{
|
|
memset(data, 0, len);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void* file_find_create(const char * path)
|
|
{
|
|
if (!path) return NULL;
|
|
GFile* parent=g_file_new_for_path(path);
|
|
GFileEnumerator* children=g_file_enumerate_children(parent, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
|
G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
|
g_object_unref(parent);
|
|
return children;
|
|
}
|
|
|
|
bool file_find_next(void* find, char * * path, bool * isdir)
|
|
{
|
|
if (!find) return false;
|
|
GFileEnumerator* children=(GFileEnumerator*)find;
|
|
|
|
GFileInfo* child=g_file_enumerator_next_file(children, NULL, NULL);
|
|
if (!child) return false;
|
|
|
|
*path=strdup(g_file_info_get_name(child));
|
|
*isdir=(g_file_info_get_file_type(child)==G_FILE_TYPE_DIRECTORY);
|
|
g_object_unref(child);
|
|
return true;
|
|
}
|
|
|
|
void file_find_close(void* find)
|
|
{
|
|
if (!find) return;
|
|
g_object_unref((GFileEnumerator*)find);
|
|
}
|
|
#endif
|
|
#endif
|