I want to write a simple image browser in C and then make a bundle for the MacOs.
The problem is that I am encountering a warning when I am trying to open a jpg file from a context menu in Finder.
I compiled my code, I prepared a iViewGtk.app bundle with Info.plist file.
The Info.plist is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>iViewGtk</string>
<key>CFBundleName</key>
<string>iViewGtk</string>
<key>CFBundleGetInfoString</key>
<string>5.0</string>
<key>CFBundleTypeName</key>
<string>3r23rfewfwefwef343f</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>****</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<string>image/jpeg</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundleIdentifier</key>
<string>net.bean.gtk.iview</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSMinimumSystemVersion</key>
<string>10.14</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleShortVersionString</key>
<string>5.0</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>bmp</string>
<string>cur</string>
<string>gif</string>
<string>icns</string>
<string>ico</string>
<string>jpeg</string>
<string>jpg</string>
<string>jpe</string>
<string>jfi</string>
<string>jfif</string>
<string>pbm</string>
<string>pgm</string>
<string>png</string>
<string>ppm</string>
<string>svg</string>
<string>svgz</string>
<string>tif</string>
<string>tiff</string>
<string>wbmp</string>
<string>webp</string>
<string>xbm</string>
<string>xpm</string>
<string>ani</string>
<string>apng</string>
<string>avif</string>
<string>avifs</string>
<string>bw</string>
<string>exr</string>
<string>hdr</string>
<string>heic</string>
<string>heif</string>
<string>jxl</string>
<string>kra</string>
<string>ora</string>
<string>pcx</string>
<string>pic</string>
<string>psd</string>
<string>ras</string>
<string>rgb</string>
<string>rgba</string>
<string>sgi</string>
<string>tga</string>
<string>xcf</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true />
<key>NSHighResolutionCapable</key>
<true />
</dict>
</plist>
Actually the Info.plist file is a copy of Info.plist file from a working application qView which can be installed using brew install qview.
In addition to that I am able to click on the bundle and open the application by clicking it.
I copied the app into Application folder and it seems that Finder recognizes it as application which can open image files. It is not grayed out:
Since I don't know how the parameter from Finder is provided to my app, I didn't implement the part which opens and displays the image. I think, that it shouldn't be a problem for MacOS, should be? The main.c looks as follows:
int main(int argc, char **argv)
{
struct ApplicationContext *appContext = malloc(sizeof(appContext));
GtkApplication *app;
int status;
app = gtk_application_new("net.bean.app", G_APPLICATION_HANDLES_OPEN);
g_signal_connect(app, "activate", G_CALLBACK(activate), appContext);
g_signal_connect(app, "open", G_CALLBACK(app_open), appContext);
status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
Could someone tell me why MacOS displays the warning?
Is this somehow related to some MacOs security policies which are forbidding to open a file to an unknown app?
Thank you.
It's not sufficient to have a .plist file, there is also handling code necessary.
By the way, this is also the case for a regular Cocoa Objective-C applications. If none of the corresponding optional NSApplicationDelegate methods (such as application:openURLs: and friends) is implemented there, the dialog shown in the question appears.
Neither from the sample code provided nor from the tags used can one conclude whether the question refers to GTK3 or GTK4 - therefore this answer covers both cases.
GTK4
GTK4 already supports macOS as a backend. Looking for a corresponding NSApplicationDelegate delegate method in gtkapplication-quartz.c one can see application:openFiles:, so the support is there.
However in line 164 one can see that the delegate is only set if the boolean register_session is true, see the check in line 161.
Following that trace backwards via gtk_application_impl_startup, gtk_application_startup in gtkapplication.c etc. one can see that register_session is a property of the application.
Basically, we need to set this to true to make sure the delegate is set and we are notified of open file attempts.
The property can then be set something like this:
GValue val = G_VALUE_INIT;
g_value_init (&val, G_TYPE_BOOLEAN);
g_value_set_boolean(&val, true);
g_object_set_property(G_OBJECT(app), "register-session", &val);
g_value_unset (&val);
Self-contained GTK4 example
Following on from the above, it could be written something like this:
#include <gtk/gtk.h>
static void
activate (GtkApplication *app) {
GtkWidget *window = gtk_application_window_new (app);
GtkWidget *button = gtk_button_new_with_label ("Close App");
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_close), window);
gtk_window_set_child (GTK_WINDOW (window), button);
gtk_window_present (GTK_WINDOW (window));
}
static void
app_open (GApplication *application, GFile **files, gint n_files, const gchar *hint) {
for (gint i = 0; i < n_files; i++) {
gchar *uri = g_file_get_uri (files[i]);
g_print ("open %s\n", uri);
g_free (uri);
}
}
int main(int argc, char **argv) {
GtkApplication *app = gtk_application_new("net.bean.app", G_APPLICATION_HANDLES_OPEN);
GValue val = G_VALUE_INIT;
g_value_init (&val, G_TYPE_BOOLEAN);
g_value_set_boolean(&val, true);
g_object_set_property(G_OBJECT(app), "register-session", &val);
g_value_unset (&val);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
g_signal_connect(app, "open", G_CALLBACK(app_open), NULL);
int status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
CFBundleTypeExtensions is deprecated
This should work with your .plist file, however CFBundleTypeExtensions is deprecated since OS X v10.5., see https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
For viewing .jpeg files, you should rather use something like the following:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>JPEG File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.jpeg</string>
</array>
</dict>
</array>
GTK4 Test
After creating the iViewGTK app bundle, you can run a quick test with the above test program - if you run it from the command line, you can even see the output of prinft on the console:
iViewGTK.app/Contents/MacOS/iViewGTK
When you open a file by right-clicking a .jpeg file in the Finder and choosing Open With/iViewGTK from the context menu, the following appears:
open file:///Users/stephan/tmp/gtk/testsuite/gdk/image-data/iamge-cmyk.jpeg
So there is no error dialog and the file path is successfully passed to the GTK application.
GTK3
In the case of a GTK3 application, there are important notes at https://www.gtk.org/docs/installations/macos:
Linking with GTK’s Quartz backend connects your application to the Mac’s native display manager, keyboard, and pointing device. With a little extra code and gtk-mac-integration you can:
...
Receive open events from Finder.
...
Completely self-contained GTK3 example
#include <gtk/gtk.h>
#include <assert.h>
#include <gtkosxapplication.h>
static GtkWidget *
new_window(void) {
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
assert (window != NULL);
gtk_widget_show_all(window);
return window;
}
static gboolean
app_open_file_cb (GtkosxApplication *app, gchar *path, gpointer user_data) {
printf("open file '%s'\n", path);
//do sth with file
return TRUE;
}
int main(int argc, char **argv) {
gtk_init (&argc, &argv);
GtkApplication *app = g_object_new (GTKOSX_TYPE_APPLICATION, NULL);
GtkWidget *window = new_window();
g_signal_connect (app, "NSApplicationOpenFile",
G_CALLBACK (app_open_file_cb), NULL);
gtkosx_application_ready (app);
gtk_main ();
g_object_unref(app);
return 0;
}
GTK3 Test
The same test as above, if you select Open with/iViewGTK in the Finder, the following appears:
So again no error dialog is shown and the file path is successfully passed to the GTK application.
Related
Using GTK3 on MacOS, after minimizing the window, the dock icon does not restore the window. I have to use the "Show All Windows" selection from the dock icon menu.
Does anyone have a work-around or know something to make the dock icons work?
I would be happy with some simple objective-c code that would make the dock icons work.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <getopt.h>
#include <unistd.h>
#include <gtk/gtk.h>
static void testBuildUI (void);
gboolean testMainLoop (void);
int
main (int argc, char *argv[])
{
gtk_init (&argc, NULL);
testBuildUI ();
testMainLoop ();
return 0;
}
static void
testBuildUI (void)
{
GtkWidget *window;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
assert (window != NULL);
gtk_widget_show_all (window);
}
int
testMainLoop (void)
{
while (1) {
gtk_main_iteration_do (FALSE);
while (gtk_events_pending ()) {
gtk_main_iteration_do (FALSE);
}
sleep (1);
}
return 0;
}
There is no special code or Objective-C needed if you use gtk_application_new instead of gtk_init and start your main loop with g_application_run.
The documentation states the following:
Currently, GtkApplication handles GTK+ initialization, application uniqueness, session management, provides some basic scriptability and desktop shell integration by exporting actions and menus and manages a list of toplevel windows whose life-cycle is automatically tied to the life-cycle of your application.
see https://docs.gtk.org/gtk3/class.Application.html
Self contained test program
Your code slightly adapted to the above points might look like the following:
#include <gtk/gtk.h>
#include <assert.h>
static void testBuildUI(GApplication *app, gpointer data);
int main(int argc, char *argv[]) {
printf("gtk version: %d.%d.%d\n", gtk_major_version, gtk_minor_version, gtk_micro_version);
GtkApplication *app = gtk_application_new("com.example.MyApp",G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(testBuildUI), NULL);
g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return 0;
}
static void testBuildUI(GApplication *app, gpointer data) {
GtkWidget *window;
window = gtk_application_window_new(GTK_APPLICATION(app));
assert (window != NULL);
gtk_widget_show_all(GTK_WIDGET(window));
}
If you click on the dock icon of this test program, the window will restore as expected.
Alternative solution
If you want to avoid GApplication/GtkApplication, you could also use the gtk-mac-integration library instead, see https://github.com/jralls/gtk-mac-integration.
The library is rather small, only a few thousand lines of C code, so you can also easily take a look at how it works internally.
There is an important hint:
Older Gtk applications that aren't based around GApplication can be adapted to use the macOS menu bar and to integrate with the Dock using GtkOSXApplication. The Gtk API that GtkOSXApplication uses is deprecated in Gtk3 and has been removed from Gtk4. Since macOS integration is available directly in Gtk using GApplication GtkOSXIntegration will not be updated for the new menu API and is deprecated. Application maintainers are strongly encouraged to redesign their programs around GApplication, particularly before migrating to Gtk4.
see https://wiki.gnome.org/Projects/GTK/OSX/Integration
The integration is easy: after gtk_init create an application object. After setting up the window call gtkosx_application_ready with the application object.
As a side note, there is also a slightly more complex example in the src folder called test-integration.c under the Github link mentioned above that shows a more advanced menu and dock integration.
Finally, your program only minimally changed to work with the gtk-mac-integration lib:
#include <gtk/gtk.h>
#include <gtkosxapplication.h>
#include <assert.h>
static void testBuildUI(void);
static void testMainLoop(void);
static void
testBuildUI(void) {
GtkWidget *window;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
assert (window != NULL);
gtk_widget_show_all(window);
}
void
testMainLoop(void) {
while (1) {
gtk_main_iteration_do(FALSE);
while (gtk_events_pending()) {
gtk_main_iteration_do(FALSE);
}
sleep(1);
}
}
int
main(int argc, char *argv[]) {
printf("gtk version: %d.%d.%d\n", gtk_major_version, gtk_minor_version, gtk_micro_version);
gtk_init(&argc, &argv);
GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
testBuildUI();
gtkosx_application_ready(theApp);
testMainLoop();
g_object_unref(theApp);
return 0;
}
I installed the library using brew install gtk-mac-integration and linked the C program with libgtkmacintegration-gtk3.dylib. Also in this variant, the window is restored as expected. when you click on the dock icon of the program.
I'm following the instructions of the official Raspberry Pi book called An introduction to C & GUI Programming (link).
It uses GTK2 to create a gui in C.
I encountered some problems trying the code that should save a file. Here the code of the book (same I used):
#include <gtk/gtk.h>
static void save_file (GtkWidget *btn, gpointer ptr)
{
GtkWidget *sch = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (ptr), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", 0, "OK", 1, NULL);
if (gtk_dialog_run (GTK_DIALOG (sch)) == 1)
{
printf ("%s selected\n", gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (sch)));
}
gtk_widget_destroy (sch);
}
void end_program (GtkWidget *wid, gpointer ptr)
{
gtk_main_quit ();
}
int main (int argc, char * argv[])
{
gtk_init (&argc, &argv);
GtkWidget *win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
GtkWidget *btn = gtk_button_new_with_label ("Close window");
g_signal_connect (btn, "clicked", G_CALLBACK (end_program), NULL);
g_signal_connect (win, "delete_event", G_CALLBACK (end_program), NULL);
GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
gtk_container_add (GTK_CONTAINER (win), box);
GtkWidget *fc_btn = gtk_button_new_with_label ("Save file");
g_signal_connect (fc_btn, "clicked", G_CALLBACK (save_file), win);
gtk_box_pack_start (GTK_BOX (vbox), fc_btn, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox), btn, TRUE, TRUE, 0);
gtk_widget_show_all (win);
gtk_main ();
return 0;
}
The books says that this program should open a window with a button that, if clicked, opens a new window where I can insert the name of the file and with OK I should be able to save it.
The resulting file path is printed correctly inside the terminal.
If I enter inside the path where I saved the file, the file doesn't exist! It's not hidded neither saved with a different name.
Is there something missing in this piece of code?
I left you a comment above. As noted, I reviewed the sample code in the PDF version of the book in your link. The purpose of that code is to present a file chooser dialog widget and confirm the name of the file you either select or type in by printing the full path in the console. The program, as is, has no mechanism for actually writing a file out to your storage medium. So that is why you do not see a file when you view the folder on your system. If you want as a minimum, a file actually written, a little more code needs to be added. Just to give you one really simple example of what that might look like, I made a revision to the "save_file" function in your sample program as noted in the following code snippet.
static void save_file (GtkWidget *btn, gpointer ptr)
{
GtkWidget *sch = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (ptr), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", 0, "OK", 1, NULL);
if (gtk_dialog_run (GTK_DIALOG (sch)) == 1)
{
printf ("%s selected\n", gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (sch)));
char cmd[1024];
strcpy(cmd, "touch ");
strcat(cmd, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (sch)));
system(cmd);
}
gtk_widget_destroy (sch);
}
FYI, I built this revised program on a Linux system using the "touch" command which will either update an existing file's timestamp or create a new empty file and the "system" command which allows one to execute a program as though one was using a terminal. You mentioned a Raspberry Pi system, which I believe uses Linux or a Linux like operating system, so these commands should work.
For further examples and tutorials, you might want to check out some videos out on the web. The following link is not a specific recommendation, but it was one of the first videos I found out on the web that walks through the various steps of C coding with GTK including references to the GTK file chooser dialog.
"https://www.youtube.com/watch?v=EdJVkr87LSk&list=PLMkSWKN9VsZH562FmV8sMvMu_sVZsYAt6"
You might want to review those videos to see if they might help you.
I hope that clarifies things for you.
Regards.
I'v been seaching google for 2 weeks now.
2 textentry in a gride.
(ex: userid, password)
Glade let me design that no problem...
For one textentry widget, it working.
I'm compiling/linking with mysql
so I would like to call functions, stored procedures
with entry1 and entry2.
Please help
thanks
:EDIT:
15:18 4 may 2019
I found a conclusive solution.
(But it's throwing seg fault)
bit of code following :
this is some kind of a transcription of the video into text (C code).
https://www.youtube.com/watch?v=_yTmW1QG3uk
obviously according to my goal really...
it's kind of working but let say you want to use the same
instance a second time, "seg fault"
I'm sure I will find the problem (gtk_main_quit) or something,
but the textentry multiples lines is solved :P
here's the code :
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <mysql/mysql.h>
GtkEntry *userid, *password;
static void Button_Pressed(GtkWidget *w, gpointer *data){
/* char *userid, *password;*/ //seg fault
/* userid[0]='\0';
password[0]='\0';*/
userid=gtk_entry_get_text(userid);
password=gtk_entry_get_text(password);
g_print("%s\n\r%s\n\r",userid, password);
}
static void CreateWindow(GtkApplication *myapp, gpointer *user_data){
GtkWidget *window;
window=gtk_application_window_new(myapp);
gtk_window_set_title(GTK_WIDGET(window), "Double Entry Solution");
gtk_window_set_default_size(GTK_WINDOW(window),400,400);
GtkWidget *vbox=gtk_vbox_new(FALSE,0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show(vbox);
// userid pack
userid=gtk_entry_new();
gtk_box_pack_start(GTK_CONTAINER(vbox), userid, TRUE, TRUE,0);
gtk_widget_show(userid);
GtkWidget *hbox=gtk_hbox_new(TRUE,0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
// password pack
password=gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox), password, TRUE, TRUE,0);
gtk_widget_show(password);
GtkWidget *submit=gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_pack_start(GTK_BOX(vbox), submit, TRUE, TRUE,0);
GtkWidget *button=gtk_button_new_with_label("Login");
g_signal_connect(button, "clicked", G_CALLBACK(Button_Pressed), NULL);
gtk_container_add(GTK_CONTAINER(submit), button);
gtk_widget_show(button);
gtk_widget_show_all(window);
}
int main(int argc, char** argv){
GtkApplication *doubleentry;
doubleentry=gtk_application_new("smdelectro.business.site.doubleentry", G_APPLICATION_FLAGS_NONE);
g_signal_connect(doubleentry, "activate", G_CALLBACK(CreateWindow), NULL);
g_application_run(G_APPLICATION(doubleentry), argc, argv);
g_object_unref(doubleentry);
return (EXIT_SUCCESS);
}
The code below work :
#include <gtk/gtk.h>
int main(int argc, char **argv)
{
// Init
gtk_init(&argc,&argv);
// Create widgets
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkGrid *grid = GTK_GRID(gtk_grid_new());
// Attach entries
gtk_grid_attach(grid,gtk_entry_new(),0,0,1,1);
gtk_grid_attach(grid,gtk_entry_new(),0,1,1,1);
// Add the grid in the window
gtk_container_add(GTK_CONTAINER(window),GTK_WIDGET(grid));
// Dirty way to force clean termination when window is closed
g_signal_connect(window,"delete-event",G_CALLBACK(gtk_main_quit),NULL);
// Show **everythings** (not only the window)
gtk_widget_show_all(window);
// Main loop
gtk_main();
return 0;
}
Without any source code I can't provide more help, could you post it please.
A common trap in GTK+3 is creating widget without showing them. If you replace gtk_widget_show_all with gtk_widget_show you will see a window without widgets, they are here but not displayed, because by default the visible property is set to FALSE.
finally,
creating c file with reference to xml is ONE way, it's cool, thanks Glade.
Creating pure C Gtk3 is pain... omg
xml parser to generate c code...
aw..
BRB
I'm following this tutorial. I'm currently at the Building applications part. There is one thing that baffles me, and that is how application window gets created in the first place. Let me explain.
This is how the program starts:
#include <gtk/gtk.h>
#include "exampleapp.h"
int
main (int argc, char *argv[])
{
return g_application_run (G_APPLICATION (example_app_new ()), argc, argv);
}
This is easy. The application is started using g_application_run function. The function takes three arguments: an app, argument count and argument vector. Let's see how this app is created.
#include <gtk/gtk.h>
#include "exampleapp.h"
#include "exampleappwin.h"
struct _ExampleApp
{
GtkApplication parent;
};
G_DEFINE_TYPE(ExampleApp, example_app, GTK_TYPE_APPLICATION);
static void
example_app_init (ExampleApp *app)
{
}
static void
example_app_activate (GApplication *app)
{
ExampleAppWindow *win;
win = example_app_window_new (EXAMPLE_APP (app));
gtk_window_present (GTK_WINDOW (win));
}
static void
example_app_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *hint)
{
GList *windows;
ExampleAppWindow *win;
int i;
windows = gtk_application_get_windows (GTK_APPLICATION (app));
if (windows)
win = EXAMPLE_APP_WINDOW (windows->data);
else
win = example_app_window_new (EXAMPLE_APP (app));
for (i = 0; i < n_files; i++)
example_app_window_open (win, files[i]);
gtk_window_present (GTK_WINDOW (win));
}
static void
example_app_class_init (ExampleAppClass *class)
{
G_APPLICATION_CLASS (class)->activate = example_app_activate;
G_APPLICATION_CLASS (class)->open = example_app_open;
}
ExampleApp *
example_app_new (void)
{
return g_object_new (EXAMPLE_APP_TYPE,
"application-id", "org.gtk.exampleapp",
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
}
The line G_DEFINE_TYPE(ExampleApp, example_app, GTK_TYPE_APPLICATION); aliases ExampleApp to GtkApplication (but in a smart way, such that types, variables, etc. associated with GtkApplication are now associated with ExampleApp).
Let's now take a look at ExampleApp *example_app_new(void) function. This function actually returns GtkApplication *, since we associated ExampleApp with GtkApplication. Next, this function calls and returns a new object by calling g_object_new function. That function takes the following arguments:
EXAMPLE_APP_TYPE, which is just GTK_TYPE_APPLICATION
"application-id", which tells that the next argument is the ID of an applicaton
"org.gtk.exampleapp", the ID
"flags", which tells that the next argument is a flag
"G_APPLICATION_HANDLES_OPEN", a flag
NULL, terminator
g_object_new, called like this, returns GtkApplication with the ID of "org.gtk.exampleapp" and with a flag "G_APPLICATION_HANDLES_OPEN". After the program goes back to example_app_new, it exits it and returns a new object of type ExampleApp * A.K.A. GtkApplication * to main. The new app is cast to GApplication in g_application_run using G_APPLICATION macro.
You have now seen what I understand. Now you'll see what I don't understand.
The tutorial linked at the top of this question says that this creates an empty window. In earlier parts of the tutorial (like this one) we used g_signal_connect to call a function when an app is run. For example,
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
calls the function activate when the app app is run using g_application_run. Function activate will in turn usually create a window and populate it either by itself or by calling other functions. That is what baffles me: there is no such thing in our example app program. How then a window gets created?
The catch is in overriding activate and open functions. It says here:
To handle these two cases, we override the activate() vfunc, which gets called when the application is launched without commandline arguments, and the open() vfunc, which gets called when the application is launched with commandline arguments.
Therefore, the function
static void
example_app_activate (GApplication *app)
{
ExampleAppWindow *win;
win = example_app_window_new (EXAMPLE_APP (app));
gtk_window_present (GTK_WINDOW (win));
}
is called when the application is run without arguments.
static void
example_app_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *hint)
{
GList *windows;
ExampleAppWindow *win;
int i;
windows = gtk_application_get_windows (GTK_APPLICATION (app));
if (windows)
win = EXAMPLE_APP_WINDOW (windows->data);
else
win = example_app_window_new (EXAMPLE_APP (app));
for (i = 0; i < n_files; i++)
example_app_window_open (win, files[i]);
gtk_window_present (GTK_WINDOW (win));
}
is called when there are given arguments.
I have a need to write a GTK application in C that does some animation using Cairo that will render into a GTK widget that exists in another running application. The idea is to do the very same thing you can do with VLC and Mplayer. For example Mplayer has the -wid option:
-wid (also see -guiwid) (X11, OpenGL and DirectX only)
This tells MPlayer to attach to an existing window. Useful to embed MPlayer in a browser (e.g. the plugger extension). This option
fills the given window completely, thus aspect scaling, panscan, etc
are no longer handled by MPlayer but must be managed by the
application that created the window.
With this Mplayer option you can create a GTK application with a GTKImage widget, get it's Xid and then play a movie in the GTK application using Mplayer with the Xid specified.
I'm trying to do the same thing except render/draw into the external window using Cairo. Anybody have suggestions or better yet a small code sample?
Take a look to the GtkSocket and GtkPlug classes.
The main program will create a GtkSocket and the XID you can pass to the other program will be returned by the function gtk_socket_get_id(). Then the other program will use it as argument to the gtk_plug_new() function. All the render will be done in children of this new GtkPlug object.
UPDATE: Well, if you want... here it is a minimal example of GtkSocket/GtkPlug. You don't say if you are using GTK+2 or GTK+3, so I'm assuming version 2.
server.c:
#include <gtk/gtk.h>
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *sck = gtk_socket_new();
gtk_container_add(GTK_CONTAINER(wnd), sck);
gtk_window_set_default_size(GTK_WINDOW(wnd), 400, 300);
gtk_widget_show_all(wnd);
GdkNativeWindow nwnd = gtk_socket_get_id(GTK_SOCKET(sck));
g_print("%lu\n", nwnd);
gtk_main();
return 0;
}
client.c:
#include <stdlib.h>
#include <gtk/gtk.h>
#include <cairo/cairo.h>
#include <math.h>
gboolean OnDraw(GtkWidget *w, GdkEvent *ev, gpointer data)
{
GtkAllocation size;
gtk_widget_get_allocation(w, &size);
cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(w));
cairo_set_source_rgb(cr, 1, 0, 0);
cairo_arc(cr, size.width/2, size.height/2, size.height/2, 0, 2*M_PI);
cairo_fill(cr);
cairo_destroy(cr);
return TRUE;
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
GdkNativeWindow nwnd = strtoul(argv[1], NULL, 10);
GtkWidget *plug = gtk_plug_new(nwnd);
GtkWidget *canvas = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(plug), canvas);
g_signal_connect(canvas, "expose-event", (GCallback)OnDraw, NULL);
gtk_widget_show_all(plug);
gtk_main();
return 0;
}
The XID to be used is printed by the server and has to be copied/pasted as argument to the client:
$ ./server
60817441
^Z
[1]+ Stopped ./server
$ bg
$ ./client 60817441