Gtk, runtime menu - gtk

I have runtime-created menu based on words in selected gtkTreeView row.
gboolean
menu_RELEASE(GtkObject *object, GdkEvent *event, gpointer user_data)
{
if (strlen(user_data) > 0)
{
gtk_entry_set_text(GTK_ENTRY(entry1), user_data);
gtk_widget_grab_focus(entry1);
}
else
main_art(get_sifra());
return TRUE;
}
gboolean
treeview1_BUTTONRELEASE(GtkWidget *widget, GdkEventButton *event, gpointer *user_data)
{
if (event->type == GDK_BUTTON_RELEASE && event->button == 3)
{
char *ntext;
treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
if (gtk_tree_selection_get_selected(treesel, &model ,&iter))
{
gtk_tree_model_get(model, &iter, cNaziv, &ntext, -1);
GtkWidget *menu, *menu_item;
menu = gtk_menu_new();
char *sresult = NULL;
sresult = strtok(ntext, " ");
while(sresult != NULL)
{
if (strlen(sresult)>1)
{
menu_item = gtk_menu_item_new_with_label(sresult);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
g_signal_connect(G_OBJECT(menu_item), "button-release-event", G_CALLBACK(menu_RELEASE), (gpointer)sresult);
}
sresult = strtok(NULL, " ");
}
menu_item = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
//
menu_item = gtk_image_menu_item_new_with_label("Uredi...");
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), GTK_WIDGET(gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU)));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
g_signal_connect(G_OBJECT(menu_item), "button-release-event", G_CALLBACK(menu_RELEASE), (gpointer)"");
//
gtk_widget_show_all(menu);
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
return TRUE;
}
}
return FALSE;
}
When menu item was released action from "menu_RELEASE" should appear.
But what happened?
Menu don't dissappear, stay's visible and active.
What is wrong with my code?

You're not supposed to connect to mouse-button signals of the items, that's being too low-level. Your handler is "swallowing" the mouse button signal, preventing GTK+ from handling it.
Use the activate signal.

Related

GTK Tree View # How to remove or hide the toggle button in column

There is defined a GTK tree view and one of the column is rendered the toggle button. Now every row is showing the toggle button, is there a way to remove or hide the toggle button from some row completely. For example I have following example
#include <gtk/gtk.h>
enum {
COL_NUM_LIST = 0,
COL_TOGGLE,
COL_STRING,
NUM_COLS
};
static GtkTreeModel*
create_and_fill_model(void) {
GtkTreeStore *treestore;
GtkTreeIter toplevel, child;
treestore = gtk_tree_store_new(NUM_COLS,
G_TYPE_STRING,
G_TYPE_BOOLEAN,
G_TYPE_STRING);
/* Append a top level row and leave it empty */
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel,
COL_NUM_LIST, "1",
COL_TOGGLE, TRUE,
COL_STRING, "Foo Bar",
-1);
/* Append a second top level row, and fill it with some data */
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel,
COL_NUM_LIST, "2",
COL_TOGGLE, TRUE,
COL_STRING, "", // empty
-1);
/* Append a child to the second top level row, and fill in some data */
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child,
COL_NUM_LIST, "3",
COL_TOGGLE, FALSE,
COL_STRING, "Not needed toggle button here",
-1);
return GTK_TREE_MODEL(treestore);
}
void string_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) {
char *string;
gtk_tree_model_get(model, iter, COL_STRING, &string, -1);
g_object_set(renderer, "text", string, NULL);
}
void toggle_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) {
int bol;
gtk_tree_model_get(model, iter, COL_TOGGLE, &bol, -1);
if (bol == 0)
g_object_set(renderer, "active", NULL, NULL); // Can we somehow null or remove toggle button
else
g_object_set(renderer, "active", TRUE, NULL);
}
static GtkWidget*
create_view_and_model(void) {
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkWidget *view;
GtkTreeModel *model;
view = gtk_tree_view_new();
// --- Column #1 ---
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "#");
/* pack tree view column into tree view */
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
// toggle renderer
renderer = gtk_cell_renderer_text_new();
/* pack cell renderer into tree view column */
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_add_attribute(col, renderer, "text", COL_NUM_LIST);
// --- Column #2 ---
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "Toggle");
/* pack tree view column into tree view */
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
// toggle renderer
renderer = gtk_cell_renderer_toggle_new();
/* pack cell renderer into tree view column */
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(col, renderer,
toggle_cell_data_func, NULL, NULL);
// --- Column #3 ---
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "String");
/* pack tree view column into tree view */
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
// text renderer
renderer = gtk_cell_renderer_text_new();
/* pack cell renderer into tree view column */
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(col, renderer,
string_cell_data_func, NULL, NULL);
model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
g_object_unref(model); /* destroy model automatically with view */
gtk_tree_selection_set_mode(
gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
GTK_SELECTION_NONE);
return view;
}
int main(int argc, char **argv) {
GtkWidget *window;
GtkWidget *view;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "delete_event", gtk_main_quit, NULL); /* dirty */
view = create_view_and_model();
gtk_container_add(GTK_CONTAINER(window), view);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Compiled and run with GTK 3 , it creates following windows
So, I don't need toggle button at #3 leave of tree. How can I hide it.
P.S : I know that using GTK, I can set the state of button to disable or inconsistent, but can I completely hide it?
You could probably revise the properties you are setting via the "g_object_set" function as in the following code snippet.
void toggle_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
int bol;
gtk_tree_model_get(model, iter, COL_TOGGLE, &bol, -1);
if (bol == 0)
g_object_set(renderer, "visible", NULL, NULL); // Can we somehow null or remove toggle button
else
{
g_object_set(renderer, "visible", TRUE, "active", TRUE, NULL);
}
}
For the toggle button that should not appear, I utilized the "visible" property in lieu of the "active" property. That yielded the following sample image.
See if that helps.
Regards.

How to overlay a picture over a video stream with Gstreamer in C?

I want to overlay a ".png" picture over a stream coming from an IP Camera using Gstreamer.
A working pipeline for my hardware is:
gst-launch-1.0
rtspsrc location=rtsp://user:pass#IP:port/channel latency=400 ! rtph264depay !
vpudec use-vpu-memory=false ! imxvideoconvert_ipu
! video/x-raw,format=I420 ! gdkpixbufoverlay
location=/home/user/folder/image.png offset-x=100 offset-y=100 ! overlaysink
The problem comes when I try to translate this pipeline in C.
The code I wrote for this pipeline runs, but there is no video playback on the display. The player stuck itself before setting the pipeline on "playing" state.
Here, there is a simple version of my C implementation:
#include <gst/gst.h>
#include <glib.h>
#include <iostream>
typedef struct _CustomData {
GstElement *source;
GstElement *rtp;
GstElement *sink;
GstElement *vpudec;
GstElement *converter, *gdkpixbufoverlay, *capsfilter ;
GstBus *bus;
GstElement *pipeline;
GMainLoop *loop;
} CustomData;
static gboolean bus_call (GstBus *bus,
GstMessage *msg,
gpointer data)
{
GMainLoop *loop = (GMainLoop *) data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_EOS:{
g_print ("End of stream\n");
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_ERROR: {
gchar *debug;
GError *error;
gst_message_parse_error (msg, &error, &debug);
g_free (debug);
g_printerr ("Error: %s\n", error->message);
g_error_free (error);
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data) {
GstPad *sink_pad = gst_element_get_static_pad (data->rtp, "sink");
GstPadLinkReturn ret;
GstCaps *new_pad_caps = NULL;
GstStructure *new_pad_struct = NULL;
const gchar *new_pad_type = NULL;
if (gst_pad_is_linked (sink_pad)) {
goto exit;
}
new_pad_caps = gst_pad_query_caps (new_pad, NULL);
new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
new_pad_type = gst_structure_get_name (new_pad_struct);
if (!g_str_has_prefix (new_pad_type, "application/x-rtp")) {
g_print (" It has type '%s' which is not x-rtp . Ignoring.\n",
new_pad_type);
goto exit;
}
ret = gst_pad_link (new_pad, sink_pad);
if (GST_PAD_LINK_FAILED (ret)) {
g_print(" Type is '%s' but link failed.\n", new_pad_type);
} else {
g_print (" Link succeeded (type '%s').\n", new_pad_type);
}
exit:
if (new_pad_caps != NULL)
gst_caps_unref (new_pad_caps);
gst_object_unref (sink_pad);
}
int main (int argc, char *argv[]){
CustomData data;
gst_init (NULL, NULL);
data.loop = g_main_loop_new (NULL, FALSE);
// Create gstreamer elements
data.pipeline = gst_pipeline_new ("player");
data.source = gst_element_factory_make ("rtspsrc", "source");
data.rtp = gst_element_factory_make ("rtph264depay","rtp");
data.vpudec = gst_element_factory_make ("vpudec","vpudec");
data.converter = gst_element_factory_make
("imxcompositor_ipu","converter");
data.capsfilter = gst_element_factory_make ("capsfilter", "video-
rate");
data.gdkpixbufoverlay = gst_element_factory_make
("gdkpixbufoverlay","overlaytool");
data.sink = gst_element_factory_make ("overlaysink",
"videoSink");
if (!data.pipeline || !data.source || !data.rtp || !data.vpudec ||
!data.converter || !data.capsfilter || !data.gdkpixbufoverlay || !data.sink)
{
g_printerr ("One element could not be created. Exiting.\n");
return -1;
}
g_object_set (data.source, "location","rtsp://user:pass#IP:port/channel",
NULL);
g_object_set (data.source,"latency", 400 , NULL);
g_object_set (data.vpudec, "use-vpu-memory", false, NULL);
g_object_set (data.gdkpixbufoverlay,
"location","/home/user/folder/image.png", NULL);
g_object_set (data.gdkpixbufoverlay, "offset-x", 100 , NULL);
g_object_set (data.gdkpixbufoverlay, "offset-y", 100 , NULL);
GstCaps *capsFormat = gst_caps_from_string ("video/x-raw,format=I420");
g_object_set ( data.capsfilter, "caps", capsFormat, NULL);
gst_caps_unref(capsFormat);
//add all elements into the pipeline
gst_bin_add_many (GST_BIN (data.pipeline),
data.source,
data.rtp,
data.vpudec,
data.converter,
data.capsfilter,
data.gdkpixbufoverlay,
data.sink,
NULL);
// link all elements
gst_element_link_many ( data.rtp, data.vpudec , data.converter ,
data.capsfilter, data.gdkpixbufoverlay, data.sink, NULL);
g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler),
&data);
// Set the pipeline to "playing" state
GstStateChangeReturn ret;
ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr("Unable to set the pipeline to the playing state.\n");
gst_object_unref (data.pipeline);
return -1;
}
// Iterate
g_main_loop_run (data.loop);
// Out of the main loop, clean
g_print ("Returned, stopping playback\n");
gst_element_set_state (data.pipeline, GST_STATE_NULL);
g_print ("Deleting pipeline\n");
gst_object_unref (GST_OBJECT (data.pipeline));
return 0;
}
Does anyone see the problem?
Thank you
After many tries, I did figure out that in the C code I posted I have selected the wrong element, therefore the data.converter element is:
data.converter = gst_element_factory_make("imxvideoconvert_ipu ","converter");
and not imxcompositor_ipu .

Drag and drop with TTreeView in Firemonkey

I am using C++ Builder 10.2.2 Tokyo with FireMonkey (FMX).
I want to add drag and drop functionality to a TTreeView, so a user can rearrange the order of the tree items. I have added a handler to the TTreeView.OnMouseDown event, based on this Drag and Drop sample project.
With this, the program can now drag and drop to rearrange items, but it seems that there is some default behavior to move a TTreeViewItem to be a child of the TTreeViewItem it is dropped onto, instead of inserting after that item.
How can I override this default behavior, so that a TTreeViewItem is inserted at the same level in the TTreeView, and at an index 1 greater than the TTreeViewItem it is dropped onto?
Following on Abdullah's suggestion, you can achieve this by creating a custom component. Directions to create a custome component in general are here. I recommend installing it in Standard on the Tool Palette, as that's where TTreeView is.
The custom component, here called TMyTreeView, has this in the header in particular:
class PACKAGE TMyTreeView : public TTreeView
{
private:
bool IsAncestor (TTreeViewItem* oItem, TTreeViewItem* oTargetItem);
protected:
int DragDelta;
void StartDrag ();
void __fastcall DragDrop (const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point);
void __fastcall MouseDown (System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, float X, float Y);
void __fastcall MouseMove (System::Classes::TShiftState Shift, float X, float Y);
public:
__fastcall TMyTreeView(TComponent* Owner);
__fastcall ~TMyTreeView ();
TBitmap* DragBmp;
TPointF MouseDownPoint;
TTreeViewItem* DragStartItem;
__published:
};
//---------------------------------------------------------------------------
where the functions are as follows in the corresponding cpp file:
__fastcall TMyTreeView::TMyTreeView(TComponent* Owner)
: TTreeView(Owner)
{
DragBmp = NULL;
DragStartItem = NULL;
DragDelta = 5;
}
//---------------------------------------------------------------------------
__fastcall TMyTreeView::~TMyTreeView ()
{
if (DragBmp)
delete DragBmp;
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::MouseMove (System::Classes::TShiftState Shift, float X, float Y)
{
TTreeView::MouseMove (Shift, X, Y);
if ((abs (X-MouseDownPoint.X) > DragDelta) || (abs (Y-MouseDownPoint.Y) > DragDelta))
StartDrag ();
}
//---------------------------------------------------------------------------
void TMyTreeView::StartDrag ()
{
if (!AllowDrag)
return;
if (!DragStartItem)
return;
if (DragBmp)
delete DragBmp;
_di_IFMXDragDropService service;
if ((TPlatformServices::Current->SupportsPlatformService (__uuidof(IFMXDragDropService)) &&
(service = TPlatformServices::Current->GetPlatformService (__uuidof(IFMXDragDropService)))))
{
TDragObject dragData;
if (!DragStartItem)
return;
dragData.Source = DragStartItem;
DragBmp = DragStartItem->MakeScreenshot ();
dragData.Data = DragBmp;
service->BeginDragDrop ((TForm*)this->Owner, dragData, DragBmp);
DragStartItem = NULL;
}
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::MouseDown (System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, float X, float Y)
{
TTreeView::MouseDown (Button, Shift, X, Y);
if (AllowDrag)
{
DragStartItem = ItemByPoint (X, Y);
MouseDownPoint.X = X;
MouseDownPoint.Y = Y;
}
else
DragStartItem = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::DragDrop (const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point)
{
TTreeViewItem* item = ItemByPoint (Point.X, Point.Y);
if (!item)
return;
TTreeViewItem* srcItem = (TTreeViewItem*)Data.Source;
if (!srcItem)
return;
if (IsAncestor (srcItem, item))
return;
if (item->ParentItem ())
item->ParentItem ()->InsertObject (item->Index, srcItem);
else
this->InsertObject (item->Index, srcItem);
//TTreeView::DragDrop (Data, Point);
}
//---------------------------------------------------------------------------
bool TMyTreeView::IsAncestor (TTreeViewItem* oItem, TTreeViewItem* oTargetItem)
{
for (int i=0; i<oItem->Count; i++)
{
TTreeViewItem* item = oItem->Items [i];
if (item == oTargetItem)
return true;
if (IsAncestor (item, oTargetItem))
return true;
}
return false;
}
//---------------------------------------------------------------------------
After installing the custom component to your Tool Palette, you can then add it to your form as you would any other component.
Special thanks to Mike Sutton, who had code to modify an earlier version of TTreeView here.
Once added to a form, set the TMyTreeView control's AllowDrag to true.

How to implement a tcp server in GTK based application using GIOChannel

I have got below sample from stackoverflow itself.
#include <glib.h>
#include <gio/gio.h>
gchar *buffer;
gboolean
network_write(GIOChannel *source,
GIOCondition cond,
gpointer data)
{
return TRUE;
}
gboolean
network_read(GIOChannel *source,
GIOCondition cond,
gpointer data)
{
GString *s = g_string_new(NULL);
GError *error;
g_print("Inside network_read function\n");
GIOStatus ret = g_io_channel_read_line_string(source, s, NULL, &error);
if (ret == G_IO_STATUS_ERROR)
g_error ("Error reading: %s\n", error->message);
else
g_print("Got: %s\n", s->str);
return TRUE;
}
gboolean
new_connection(GSocketService *service,
GSocketConnection *connection,
GObject *source_object,
gpointer user_data)
{
GSocketAddress *sockaddr = g_socket_connection_get_remote_address(connection, NULL);
GInetAddress *addr = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(sockaddr));
guint16 port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(sockaddr));
g_print("New Connection from %s:%d\n", g_inet_address_to_string(addr), port);
GSocket *socket = g_socket_connection_get_socket(connection);
gint fd = g_socket_get_fd(socket);
g_print("Naseeb fd: %d\n", fd);
GIOChannel *channel = g_io_channel_unix_new(fd);
if(!g_io_add_watch(channel, G_IO_IN, (GIOFunc) network_read, NULL))
{
g_print("Got Error while adding network_read\n");
}
// g_io_add_watch(channel, G_IO_OUT, (GIOFunc) network_write, NULL);
return TRUE;
}
int main(int argc, char **argv) {
g_type_init();
GSocketService *service = g_socket_service_new();
GInetAddress *address = g_inet_address_new_from_string("0.0.0.0");
GSocketAddress *socket_address = g_inet_socket_address_new(address, 3001);
g_socket_listener_add_address(G_SOCKET_LISTENER(service), socket_address, G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_TCP, NULL, NULL, NULL);
g_object_unref(socket_address);
g_object_unref(address);
g_socket_service_start(service);
g_signal_connect(service, "incoming", G_CALLBACK(new_connection), NULL);
g_socket_service_start(service);
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
return 0;
}
Using this code, i am able to get the client connected to server. I confirm the same using message printed in the function new_connection
But when i send data from client, callback network_read never gets called at server. Although client side, send() API return value shows total bytes sent.
1) Is there any api missing at server side.
2) What is proper way to invoke network_write ?
You shouldn't be using GIOChannel, new_connection() already has a connection and you can just use stream = g_io_stream_get_input_stream (G_IO_STREAM (connection)); to get a GInputStream to read from.

GTK Main blocking Others threads.How to solve

I have 2 threads
1)Holds the GTK main and gtk screen display codes (code is explained below) 2)generates key events according to user rquirement
if() block i ported into my code. but result is same. Once the signal is generated .after that its not coming to 2nd thread(signal generation thread). Have put debug prints ,but its not happening Seems its waiting on gtk_main on first thread.
What my code is :
void S1(void)
{
GtkWidget *Win_1;
GtkBuilder *builder;
builder = gtk_builder_new ();
gtk_builder_add_from_file (builder, "/home/glade/glade1.glade", NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "Win_1"));
g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window));
g_signal_connect (G_OBJECT (window), "key_press_event", G_CALLBACK(kp_event), NULL);
gtk_widget_show_all(window);
gtk_main();
}
kp_event()
{
gtk_widget_destroy (window);
S2();
}
S2 is same as S1,only screen item difference.Am calling S2() from keypress handler of S1 & vice versa. Since i have no keyboards attached,need to change two screens base on some user input via sockets or something.
You may need to call gtk_main() just one time, and use gtk_widget_hide() and gtk_window_present(), instead of gtk_widget_destroy(), declaring window1 and window2 as global variables, and creating the two windows at startup. A sample code:
GtkWidget * window1;
GtkWidget * window2;
void S1() {
// create the window
window1 = GTK_WIDGET (gtk_builder_get_object (builder, "Win_1"));
// do not call gtk_main()
}
void S2() {
// create the window
window2 = GTK_WIDGET (gtk_builder_get_object (builder, "Win_2"));
// do not call gtk_main()
}
kp_event_S1() {
gtk_widget_hide(window1);
gtk_window_present(GTK_WINDOW(window2));
}
kp_event_S2() {
gtk_widget_hide(window2);
gtk_window_present(GTK_WINDOW(window1));
}
int main() {
gtk_init();
S1();
S2();
gtk_widget_hide(window2);
gtk_main();
}
If you don't want to use global variables, you can do:
GtkWidget * S1() {
// create the window
GtkWidget * window1 = GTK_WIDGET (gtk_builder_get_object (builder, "Win_1"));
return window1;
}
GtkWidget * S2() {
// create the window
GtkWidget * window2 = GTK_WIDGET (gtk_builder_get_object (builder, "Win_2"));
return window2;
}
gboolean kp_event_S1(GtkWidget * window, GdkEvent e, gpointer user_data) {
gtk_widget_hide(window);
gtk_window_present(GTK_WINDOW(user_data));
}
gboolean kp_event_S2(GtkWidget * window, GdkEvent e, gpointer user_data) {
gtk_widget_hide(window);
gtk_window_present(GTK_WINDOW(user_data));
}
int main() {
gtk_init();
GtkWidget * w1 = S1();
GtkWidget * w2 = S2();
gtk_widget_hide(w2);
g_signal_connect (G_OBJECT (w1), "key-press-event", G_CALLBACK(kp_event_S1), (gpointer)w2);
g_signal_connect (G_OBJECT (w2), "key-press-event", G_CALLBACK(kp_event_S2), (gpointer)w1);
gtk_main();
}