geany/src/socket.c

833 lines
20 KiB
C
Raw Normal View History

/*
* socket.c - this file is part of Geany, a fast and lightweight IDE
*
2012-06-18 01:13:05 +02:00
* Copyright 2006-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
* Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
* Copyright 2006 Hiroyuki Yamamoto (author of Sylpheed)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
2012-08-24 19:25:57 +02:00
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Socket setup and messages handling.
* The socket allows detection and messages to be sent to the first running instance of Geany.
* Only the first instance loads session files at startup, and opens files from the command-line.
*/
/*
* Little dev doc:
* Each command which is sent between two instances (see send_open_command and
* socket_lock_input_cb) should have the following scheme:
* command name\n
* data\n
* data\n
* ...
* .\n
* The first thing should be the command name followed by the data belonging to the command and
* to mark the end of data send a single '.'. Each message should be ended with \n.
* The command window is only available on Windows and takes no additional data, instead it
* writes back a Windows handle (HWND) for the main window to set it to the foreground (focus).
*
2011-11-20 14:48:05 +01:00
* At the moment the commands window, doclist, open, openro, line and column are available.
*
* About the socket files on Unix-like systems:
* Geany creates a socket in /tmp (or any other directory returned by g_get_tmp_dir()) and
* a symlink in the current configuration to the created socket file. The symlink is named
* geany_socket_<hostname>_<displayname> (displayname is the name of the active X display).
* If the socket file cannot be created in the temporary directory, Geany creates the socket file
* directly in the configuration directory as a fallback.
*
*/
Include what you use This is a mega-commit - because most of it had to be done in one go otherwise some commits would fail to compile - that attempts to fix a few problems with Geany's includes as well as various other related cleanups. After this change it's easier to use includes and there's little worry about which order things are included in or who includes what. Overview of changes: * Include config.h at the start of each source file if HAVE_CONFIG_H is defined (and never in headers). * Go through each source file and make the includes section generally like this: - Always config.h first as above - Then if the file has a header with the same name, include that - Then include in alphabetical order each other internal/geany header. - Then include standard headers - Then include non-standard system headers - Then include GLib/GTK+ related stuff * Doing as above makes it easier to find implicit header include dependencies and it exposed quite a few weird problems with includes or forward declarations, fix those. * Make geany.h contain not much besides some defines. - Add a little header file "app.h" for GeanyApp and move it there - Move "app" global to new "app.h" file - Move "ignore_callback" global to "callbacks.h" - Move "geany_object" global to "geanyobject.h" * Add an include in "geany.h" for "app.h" since GeanyApp used to be defined there and some plugins included this header to access GeanyApp. * Include "gtkcompat.h" everywhere instead of gtk/gtk.h so that everywhere sees the same definitions (not a problem in practice AFAIK so this could be changed back if better that way. * Remove forward declarations from previous commits as some people apparently consider this bad style, despite that it reduces inter- header dependencies. TODO: * As always, to test on win32 * As always, to test with not Autotools * Test plugins better, both builtin and geany-plugins, likely API/ABI bump * Test with various defines/flags that may change what is included * win32.[ch] not really touched since I couldn't test
2014-05-18 17:31:51 -07:00
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_SOCKET
Include what you use This is a mega-commit - because most of it had to be done in one go otherwise some commits would fail to compile - that attempts to fix a few problems with Geany's includes as well as various other related cleanups. After this change it's easier to use includes and there's little worry about which order things are included in or who includes what. Overview of changes: * Include config.h at the start of each source file if HAVE_CONFIG_H is defined (and never in headers). * Go through each source file and make the includes section generally like this: - Always config.h first as above - Then if the file has a header with the same name, include that - Then include in alphabetical order each other internal/geany header. - Then include standard headers - Then include non-standard system headers - Then include GLib/GTK+ related stuff * Doing as above makes it easier to find implicit header include dependencies and it exposed quite a few weird problems with includes or forward declarations, fix those. * Make geany.h contain not much besides some defines. - Add a little header file "app.h" for GeanyApp and move it there - Move "app" global to new "app.h" file - Move "ignore_callback" global to "callbacks.h" - Move "geany_object" global to "geanyobject.h" * Add an include in "geany.h" for "app.h" since GeanyApp used to be defined there and some plugins included this header to access GeanyApp. * Include "gtkcompat.h" everywhere instead of gtk/gtk.h so that everywhere sees the same definitions (not a problem in practice AFAIK so this could be changed back if better that way. * Remove forward declarations from previous commits as some people apparently consider this bad style, despite that it reduces inter- header dependencies. TODO: * As always, to test on win32 * As always, to test with not Autotools * Test plugins better, both builtin and geany-plugins, likely API/ABI bump * Test with various defines/flags that may change what is included * win32.[ch] not really touched since I couldn't test
2014-05-18 17:31:51 -07:00
#include "socket.h"
#include "app.h"
#include "dialogs.h"
#include "document.h"
#include "encodings.h"
#include "main.h"
#include "support.h"
#include "utils.h"
2014-08-01 12:00:20 +01:00
#include "win32.h"
Include what you use This is a mega-commit - because most of it had to be done in one go otherwise some commits would fail to compile - that attempts to fix a few problems with Geany's includes as well as various other related cleanups. After this change it's easier to use includes and there's little worry about which order things are included in or who includes what. Overview of changes: * Include config.h at the start of each source file if HAVE_CONFIG_H is defined (and never in headers). * Go through each source file and make the includes section generally like this: - Always config.h first as above - Then if the file has a header with the same name, include that - Then include in alphabetical order each other internal/geany header. - Then include standard headers - Then include non-standard system headers - Then include GLib/GTK+ related stuff * Doing as above makes it easier to find implicit header include dependencies and it exposed quite a few weird problems with includes or forward declarations, fix those. * Make geany.h contain not much besides some defines. - Add a little header file "app.h" for GeanyApp and move it there - Move "app" global to new "app.h" file - Move "ignore_callback" global to "callbacks.h" - Move "geany_object" global to "geanyobject.h" * Add an include in "geany.h" for "app.h" since GeanyApp used to be defined there and some plugins included this header to access GeanyApp. * Include "gtkcompat.h" everywhere instead of gtk/gtk.h so that everywhere sees the same definitions (not a problem in practice AFAIK so this could be changed back if better that way. * Remove forward declarations from previous commits as some people apparently consider this bad style, despite that it reduces inter- header dependencies. TODO: * As always, to test on win32 * As always, to test with not Autotools * Test plugins better, both builtin and geany-plugins, likely API/ABI bump * Test with various defines/flags that may change what is included * win32.[ch] not really touched since I couldn't test
2014-05-18 17:31:51 -07:00
#include "gtkcompat.h"
#ifndef G_OS_WIN32
# include <sys/time.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <netinet/in.h>
# include <glib/gstdio.h>
#else
# include <winsock2.h>
# include <windows.h>
# include <gdk/gdkwin32.h>
# include <ws2tcpip.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#ifdef G_OS_WIN32
#define REMOTE_CMD_PORT 49876
#define SOCKET_IS_VALID(s) ((s) != INVALID_SOCKET)
#else
#define SOCKET_IS_VALID(s) ((s) >= 0)
#define INVALID_SOCKET (-1)
#endif
#define BUFFER_LENGTH 4096
struct socket_info_struct socket_info;
#ifdef G_OS_WIN32
static gint socket_fd_connect_inet (gushort port);
static gint socket_fd_open_inet (gushort port);
static void socket_init_win32 (void);
#else
static gint socket_fd_connect_unix (const gchar *path);
static gint socket_fd_open_unix (const gchar *path);
#endif
static gint socket_fd_write (gint sock, const gchar *buf, gint len);
static gint socket_fd_write_all (gint sock, const gchar *buf, gint len);
static gint socket_fd_gets (gint sock, gchar *buf, gint len);
static gint socket_fd_check_io (gint fd, GIOCondition cond);
static gint socket_fd_read (gint sock, gchar *buf, gint len);
static gint socket_fd_recv (gint fd, gchar *buf, gint len, gint flags);
static gint socket_fd_close (gint sock);
static void send_open_command(gint sock, gint argc, gchar **argv)
{
gint i;
gchar *filename;
g_return_if_fail(argc > 1);
geany_debug("using running instance of Geany");
if (cl_options.goto_line >= 0)
{
gchar *line = g_strdup_printf("%d\n", cl_options.goto_line);
socket_fd_write_all(sock, "line\n", 5);
socket_fd_write_all(sock, line, strlen(line));
socket_fd_write_all(sock, ".\n", 2);
g_free(line);
}
if (cl_options.goto_column >= 0)
{
gchar *col = g_strdup_printf("%d\n", cl_options.goto_column);
socket_fd_write_all(sock, "column\n", 7);
socket_fd_write_all(sock, col, strlen(col));
socket_fd_write_all(sock, ".\n", 2);
g_free(col);
}
if (cl_options.readonly) /* append "ro" to denote readonly status for new docs */
socket_fd_write_all(sock, "openro\n", 7);
else
socket_fd_write_all(sock, "open\n", 5);
for (i = 1; i < argc && argv[i] != NULL; i++)
{
filename = main_get_argv_filename(argv[i]);
/* if the filename is valid or if a new file should be opened is check on the other side */
if (filename != NULL)
{
socket_fd_write_all(sock, filename, strlen(filename));
socket_fd_write_all(sock, "\n", 1);
}
else
{
g_printerr(_("Could not find file '%s'."), filename);
g_printerr("\n"); /* keep translation from open_cl_files() in main.c. */
}
g_free(filename);
}
socket_fd_write_all(sock, ".\n", 2);
}
#ifndef G_OS_WIN32
static void remove_socket_link_full(void)
{
gchar real_path[512];
gsize len;
real_path[0] = '\0';
/* read the contents of the symbolic link socket_info.file_name and delete it
* readlink should return something like "/tmp/geany_socket.499602d2" */
len = readlink(socket_info.file_name, real_path, sizeof(real_path) - 1);
if ((gint) len > 0)
{
real_path[len] = '\0';
g_unlink(real_path);
}
g_unlink(socket_info.file_name);
}
#endif
static void socket_get_document_list(gint sock)
{
gchar buf[BUFFER_LENGTH];
gint n_read;
if (sock < 0)
return;
socket_fd_write_all(sock, "doclist\n", 8);
do
{
2014-05-21 16:13:22 +02:00
n_read = socket_fd_read(sock, buf, BUFFER_LENGTH);
/* if we received ETX (end-of-text), there is nothing else to read, so cut that
* byte not to output it and to be sure not to validate the loop condition */
if (n_read > 0 && buf[n_read - 1] == '\3')
n_read--;
if (n_read > 0)
fwrite(buf, 1, n_read, stdout);
}
2014-05-21 16:13:22 +02:00
while (n_read >= BUFFER_LENGTH);
}
#ifndef G_OS_WIN32
static void check_socket_permissions(void)
{
struct stat socket_stat;
if (g_lstat(socket_info.file_name, &socket_stat) == 0)
{ /* If the user id of the process is not the same as the owner of the socket
* file, then ignore this socket and start a new session. */
if (socket_stat.st_uid != getuid())
{
const gchar *msg = _(
/* TODO maybe this message needs a rewording */
"Geany tried to access the Unix Domain socket of another instance running as another user.\n"
"This is a fatal error and Geany will now quit.");
g_warning("%s", msg);
dialogs_show_msgbox(GTK_MESSAGE_ERROR, "%s", msg);
exit(1);
}
}
}
#endif
/* (Unix domain) socket support to replace the old FIFO code
* (taken from Sylpheed, thanks)
* Returns the created socket, -1 if an error occurred or -2 if another socket exists and files
* were sent to it. */
gint socket_init(gint argc, gchar **argv)
{
gint sock;
#ifdef G_OS_WIN32
HANDLE hmutex;
HWND hwnd;
socket_init_win32();
hmutex = CreateMutexA(NULL, FALSE, "Geany");
if (! hmutex)
{
geany_debug("cannot create Mutex\n");
return -1;
}
if (GetLastError() != ERROR_ALREADY_EXISTS)
{
/* To support multiple instances with different configuration directories (as we do on
* non-Windows systems) we would need to use different port number s but it might be
* difficult to get a port number which is unique for a configuration directory (path)
* and which is unused. This port number has to be guessed by the first and new instance
* and the only data is the configuration directory path.
* For now we use one port number, that is we support only one instance at all. */
sock = socket_fd_open_inet(REMOTE_CMD_PORT);
if (sock < 0)
return 0;
return sock;
}
sock = socket_fd_connect_inet(REMOTE_CMD_PORT);
if (sock < 0)
return -1;
#else
2014-05-21 16:08:38 +02:00
gchar *display_name = NULL;
const gchar *hostname = g_get_host_name();
2014-05-21 16:08:38 +02:00
GdkDisplay *display = gdk_display_get_default();
gchar *p;
2014-05-21 16:08:38 +02:00
if (display != NULL)
display_name = g_strdup(gdk_display_get_name(display));
if (display_name == NULL)
display_name = g_strdup("NODISPLAY");
/* these lines are taken from dcopc.c in kdelibs */
if ((p = strrchr(display_name, '.')) > strrchr(display_name, ':') && p != NULL)
*p = '\0';
/* remove characters that may not be acceptable in a filename */
for (p = display_name; *p; p++)
{
if (*p == ':' || *p == '/')
*p = '_';
}
if (socket_info.file_name == NULL)
socket_info.file_name = g_strdup_printf("%s%cgeany_socket_%s_%s",
app->configdir, G_DIR_SEPARATOR, hostname, display_name);
g_free(display_name);
/* check whether the real user id is the same as this of the socket file */
check_socket_permissions();
sock = socket_fd_connect_unix(socket_info.file_name);
if (sock < 0)
{
remove_socket_link_full(); /* deletes the socket file and the symlink */
return socket_fd_open_unix(socket_info.file_name);
}
#endif
/* remote command mode, here we have another running instance and want to use it */
#ifdef G_OS_WIN32
/* first we send a request to retrieve the window handle and focus the window */
socket_fd_write_all(sock, "window\n", 7);
if (socket_fd_read(sock, (gchar *)&hwnd, sizeof(hwnd)) == sizeof(hwnd))
SetForegroundWindow(hwnd);
#endif
/* now we send the command line args */
if (argc > 1)
{
send_open_command(sock, argc, argv);
}
if (cl_options.list_documents)
{
socket_get_document_list(sock);
}
socket_fd_close(sock);
return -2;
}
gint socket_finalize(void)
{
if (socket_info.lock_socket < 0)
return -1;
if (socket_info.lock_socket_tag > 0)
g_source_remove(socket_info.lock_socket_tag);
if (socket_info.read_ioc)
{
g_io_channel_shutdown(socket_info.read_ioc, FALSE, NULL);
g_io_channel_unref(socket_info.read_ioc);
socket_info.read_ioc = NULL;
}
#ifdef G_OS_WIN32
WSACleanup();
#else
if (socket_info.file_name != NULL)
{
remove_socket_link_full(); /* deletes the socket file and the symlink */
g_free(socket_info.file_name);
}
#endif
return 0;
}
#ifdef G_OS_UNIX
static gint socket_fd_connect_unix(const gchar *path)
{
gint sock;
struct sockaddr_un addr;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
{
perror("fd_connect_unix(): socket");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
socket_fd_close(sock);
return -1;
}
return sock;
}
static gint socket_fd_open_unix(const gchar *path)
{
gint sock;
struct sockaddr_un addr;
gint val;
gchar *real_path;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
{
perror("sock_open_unix(): socket");
return -1;
}
val = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
{
perror("setsockopt");
socket_fd_close(sock);
return -1;
}
/* fix for #1888561:
* in case the configuration directory is located on a network file system or any other
* file system which doesn't support sockets, we just link the socket there and create the
* real socket in the system's tmp directory assuming it supports sockets */
real_path = g_strdup_printf("%s%cgeany_socket.%08x",
g_get_tmp_dir(), G_DIR_SEPARATOR, g_random_int());
if (utils_is_file_writable(real_path) != 0)
{ /* if real_path is not writable for us, fall back to ~/.config/geany/geany_socket_*_* */
/* instead of creating a symlink and print a warning */
g_warning("Socket %s could not be written, using %s as fallback.", real_path, path);
SETPTR(real_path, g_strdup(path));
}
/* create a symlink in e.g. ~/.config/geany/geany_socket_hostname__0 to /tmp/geany_socket.499602d2 */
else if (symlink(real_path, path) != 0)
{
perror("symlink");
socket_fd_close(sock);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, real_path, sizeof(addr.sun_path) - 1);
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind");
socket_fd_close(sock);
return -1;
}
if (listen(sock, 1) < 0)
{
perror("listen");
socket_fd_close(sock);
return -1;
}
g_chmod(real_path, 0600);
g_free(real_path);
return sock;
}
#endif
static gint socket_fd_close(gint fd)
{
#ifdef G_OS_WIN32
return closesocket(fd);
#else
return close(fd);
#endif
}
#ifdef G_OS_WIN32
static gint socket_fd_open_inet(gushort port)
{
SOCKET sock;
struct sockaddr_in addr;
gchar val;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (G_UNLIKELY(! SOCKET_IS_VALID(sock)))
{
geany_debug("fd_open_inet(): socket() failed: %d\n", WSAGetLastError());
return -1;
}
val = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
{
perror("setsockopt");
socket_fd_close(sock);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind");
socket_fd_close(sock);
return -1;
}
if (listen(sock, 1) < 0)
{
perror("listen");
socket_fd_close(sock);
return -1;
}
return sock;
}
static gint socket_fd_connect_inet(gushort port)
{
SOCKET sock;
struct sockaddr_in addr;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (G_UNLIKELY(! SOCKET_IS_VALID(sock)))
{
geany_debug("fd_connect_inet(): socket() failed: %d\n", WSAGetLastError());
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
socket_fd_close(sock);
return -1;
}
return sock;
}
static void socket_init_win32(void)
{
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != NO_ERROR)
geany_debug("WSAStartup() failed\n");
return;
}
#endif
static void handle_input_filename(const gchar *buf)
{
gchar *utf8_filename, *locale_filename;
/* we never know how the input is encoded, so do the best auto detection we can */
if (! g_utf8_validate(buf, -1, NULL))
utf8_filename = encodings_convert_to_utf8(buf, -1, NULL);
else
utf8_filename = g_strdup(buf);
locale_filename = utils_get_locale_from_utf8(utf8_filename);
if (locale_filename)
{
if (g_str_has_suffix(locale_filename, ".geany"))
{
if (project_ask_close())
main_load_project_from_command_line(locale_filename, TRUE);
}
else
main_handle_filename(locale_filename);
}
g_free(utf8_filename);
g_free(locale_filename);
}
static gchar *build_document_list(void)
{
GString *doc_list = g_string_new(NULL);
guint i;
const gchar *filename;
foreach_document(i)
{
filename = DOC_FILENAME(documents[i]);
g_string_append(doc_list, filename);
g_string_append_c(doc_list, '\n');
}
return g_string_free(doc_list, FALSE);
}
gboolean socket_lock_input_cb(GIOChannel *source, GIOCondition condition, gpointer data)
{
gint fd, sock;
gchar buf[BUFFER_LENGTH];
struct sockaddr_in caddr;
socklen_t caddr_len = sizeof(caddr);
GtkWidget *window = data;
gboolean popup = FALSE;
fd = g_io_channel_unix_get_fd(source);
sock = accept(fd, (struct sockaddr *)&caddr, &caddr_len);
/* first get the command */
while (socket_fd_gets(sock, buf, sizeof(buf)) != -1)
{
if (strncmp(buf, "open", 4) == 0)
{
cl_options.readonly = strncmp(buf+4, "ro", 2) == 0; /* open in readonly? */
while (socket_fd_gets(sock, buf, sizeof(buf)) != -1 && *buf != '.')
{
gsize buf_len = strlen(buf);
/* remove trailing newline */
if (buf_len > 0 && buf[buf_len - 1] == '\n')
buf[buf_len - 1] = '\0';
handle_input_filename(buf);
}
popup = TRUE;
}
else if (strncmp(buf, "doclist", 7) == 0)
{
gchar *doc_list = build_document_list();
if (!EMPTY(doc_list))
socket_fd_write_all(sock, doc_list, strlen(doc_list));
/* send ETX (end-of-text) so reader knows to stop reading */
socket_fd_write_all(sock, "\3", 1);
g_free(doc_list);
}
else if (strncmp(buf, "line", 4) == 0)
{
while (socket_fd_gets(sock, buf, sizeof(buf)) != -1 && *buf != '.')
{
g_strstrip(buf); /* remove \n char */
/* on any error we get 0 which should be safe enough as fallback */
cl_options.goto_line = atoi(buf);
}
}
else if (strncmp(buf, "column", 6) == 0)
{
while (socket_fd_gets(sock, buf, sizeof(buf)) != -1 && *buf != '.')
{
g_strstrip(buf); /* remove \n char */
/* on any error we get 0 which should be safe enough as fallback */
cl_options.goto_column = atoi(buf);
}
}
#ifdef G_OS_WIN32
else if (strncmp(buf, "window", 6) == 0)
{
2012-09-19 09:00:50 +03:00
# if GTK_CHECK_VERSION(3, 0, 0)
HWND hwnd = (HWND) gdk_win32_window_get_handle(gtk_widget_get_window(window));
# else
HWND hwnd = (HWND) gdk_win32_drawable_get_handle(
GDK_DRAWABLE(gtk_widget_get_window(window)));
2012-09-19 09:00:50 +03:00
# endif
socket_fd_write(sock, (gchar *)&hwnd, sizeof(hwnd));
}
#endif
}
if (popup)
{
#ifdef GDK_WINDOWING_X11
GdkWindow *x11_window = gtk_widget_get_window(window);
/* Set the proper interaction time on the window. This seems necessary to make
* gtk_window_present() really bring the main window into the foreground on some
* window managers like Gnome's metacity.
* Code taken from Gedit. */
# if GTK_CHECK_VERSION(3, 0, 0)
if (GDK_IS_X11_WINDOW(x11_window))
# endif
{
gdk_x11_window_set_user_time(x11_window, gdk_x11_get_server_time(x11_window));
}
#endif
gtk_window_present(GTK_WINDOW(window));
#ifdef G_OS_WIN32
gdk_window_show(gtk_widget_get_window(window));
#endif
}
socket_fd_close(sock);
return TRUE;
}
static gint socket_fd_gets(gint fd, gchar *buf, gint len)
{
gchar *newline, *bp = buf;
gint n;
if (--len < 1)
return -1;
do
{
if ((n = socket_fd_recv(fd, bp, len, MSG_PEEK)) <= 0)
return -1;
if ((newline = memchr(bp, '\n', n)) != NULL)
n = newline - bp + 1;
if ((n = socket_fd_read(fd, bp, n)) < 0)
return -1;
bp += n;
len -= n;
} while (! newline && len);
*bp = '\0';
return bp - buf;
}
static gint socket_fd_recv(gint fd, gchar *buf, gint len, gint flags)
{
if (socket_fd_check_io(fd, G_IO_IN) < 0)
return -1;
return recv(fd, buf, len, flags);
}
static gint socket_fd_read(gint fd, gchar *buf, gint len)
{
if (socket_fd_check_io(fd, G_IO_IN) < 0)
return -1;
#ifdef G_OS_WIN32
return recv(fd, buf, len, 0);
#else
return read(fd, buf, len);
#endif
}
static gint socket_fd_check_io(gint fd, GIOCondition cond)
{
struct timeval timeout;
fd_set fds;
#ifdef G_OS_UNIX
gint flags;
#endif
timeout.tv_sec = 60;
timeout.tv_usec = 0;
#ifdef G_OS_UNIX
/* checking for non-blocking mode */
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
{
perror("fcntl");
return 0;
}
if ((flags & O_NONBLOCK) != 0)
return 0;
#endif
FD_ZERO(&fds);
#ifdef G_OS_WIN32
FD_SET((SOCKET)fd, &fds);
#else
FD_SET(fd, &fds);
#endif
if (cond == G_IO_IN)
{
select(fd + 1, &fds, NULL, NULL, &timeout);
}
else
{
select(fd + 1, NULL, &fds, NULL, &timeout);
}
if (FD_ISSET(fd, &fds))
{
return 0;
}
else
{
geany_debug("Socket IO timeout");
return -1;
}
}
static gint socket_fd_write_all(gint fd, const gchar *buf, gint len)
{
gint n, wrlen = 0;
while (len)
{
n = socket_fd_write(fd, buf, len);
if (n <= 0)
return -1;
len -= n;
wrlen += n;
buf += n;
}
return wrlen;
}
gint socket_fd_write(gint fd, const gchar *buf, gint len)
{
if (socket_fd_check_io(fd, G_IO_OUT) < 0)
return -1;
#ifdef G_OS_WIN32
return send(fd, buf, len, 0);
#else
return write(fd, buf, len);
#endif
}
#endif