The main goal
I wrote a python simple program that adds a file to the recent files list in GTK3. It gets called when vim opens a file. It's working fine, but vim's startup time is multiplied by 10. Now I'm trying to port it to C so as to improve this flaw. Here is a demo of the python script I'm trying to port :
from gi import require_version
require_version('Gtk', '3.0')
from gi.repository import Gtk
manager = Gtk.RecentManager()
recent_data = Gtk.RecentData()
recent_data.app_name = "vim"
recent_data.mime_type = "text/plain"
recent_data.app_exec = "/usr/bin/vim"
manager.add_full("file:///home/lafleur/tweaks.txt", recent_data)
What I attempted
See the code sample below. It compiles fine, but I get a CRITICAL warning when I run it, and then the process succeeds, but the file doesn't show up in the recent files in Nautilus.
Here is the traceback :
$ ./a.out
adding file:///home/lafleur/tweaks.txt to recent files
(process:17646): GLib-GObject-CRITICAL **: 12:37:32.034: g_object_get: assertion 'G_IS_OBJECT (object)' failed
file added to recent files.
I'm unable to tell what went wrong. I followed docs in GNOME's GTK3 documentation. Those docs state that the mandatory arguments to gtk_recent_manager_add_full () are the gtk_recent_manager, a uri and a GtkRecentData object holding the file's MIME type, the application name and its callback. When compiled, the process complains that it needs an application description, which I added in the sample (see below). I found g_object_get ()'s definition here, but that doesn't give me any clue.
The question
My question is : how can I know what's happening and why the process fails to add the existing /home/lafleur/tweaks.txt to Nautilus' recent files listing ? How can I know what in my code is not a valid GObject ? Did I miss some initialization, as stated in this SO answer ?
Here is the code sample :
#include <gtk/gtk.h>
int main (int argc, char **argv)
{
GtkRecentData recent_data;
GtkRecentManager *manager;
GError *error = NULL;
gchar *uri;
gboolean retval;
uri = g_filename_to_uri ("/home/lafleur/tweaks.txt", NULL, &error);
manager = gtk_recent_manager_get_default ();
if (error) {
g_warning ("%s", error->message);
g_error_free (error);
} else {
recent_data.mime_type = "text/plain";
recent_data.app_name = "vim";
recent_data.app_exec = "/usr/bin/vim";
recent_data.description = "the vim editor";
g_print ("adding %s to recent files\n", uri);
retval = gtk_recent_manager_add_full (
manager,
uri,
&recent_data
);
if (retval == TRUE) {
g_print ("file added to recent files.\n");
} else {
g_warning ("there was a problem.\n");
}
g_free (uri);
}
return retval;
When using GTK API from C, you need to initialize GTK itself, with something like:
gtk_init (&argc, &argv);
where argc and argv are pointers to the arguments counter and vector that you get in your main. If you don't do this, anything that follows is undefined behaviour.
The Python bindings, for reasons of backward compatibility in the days of GTK 1 and 2, call gtk_init() automatically when imported.
Additionally, GtkRecentManager queues updates within the GTK main loop, to coalesce multiple writes into one; this is not normally an issue in GUI applications, but if you're writing a CLI tool, you will need to also spin the main loop until the GtkRecentManager emits the "changed" signal.
Your program will work once you modify it to call gtk_init() first, and then spin the main loop until the write is done, e.g.:
#include <stdlib.h>
#include <gtk/gtk.h>
int main (int argc, char **argv)
{
GError *error = NULL;
char *uri = g_filename_to_uri ("/home/lafleur/tweaks.txt", NULL, &error);
// Bail out early in case of error
if (error != NULL) {
g_warning ("%s", error->message);
g_error_free (error);
return EXIT_FAILURE;
}
// You can pass (NULL, NULL) if you don't have arguments; if you
// want to deal with the possibility of not having a display
// connection, you can use gtk_init_check() instead.
gtk_init (&argc, &argv);
// Create the recent manager
GtkRecentManager *manager = gtk_recent_manager_get_default ();
// Create a main loop; the recent manager will schedule writes
// within the loop, so it can coalesce multiple operations.
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
// Once we receive the "changed" signal from the recent manager
// we stop the main loop; we use the "swapped" variant so that
// the callback will be invoked with the signal's user data
// as the first argument; in this case, we're going to call
// g_main_loop_quit() on the loop object
g_signal_connect_swapped (manager, "changed",
G_CALLBACK (g_main_loop_quit),
loop);
GtkRecentData recent_data = {
.mime_type = "text/plain",
.app_name = "vim",
// The "app_exec" key should be the command line needed to open
// the file you're adding; the %f escape sequence means "use the
// path of the file", whereas the %U escape sequence means "use
// the URL of the file".
.app_exec = "/usr/bin/vim %f",
.description = "the vim editor",
};
g_print ("adding %s to recent files\n", uri);
gboolean retval = gtk_recent_manager_add_full (
manager,
uri,
&recent_data
);
// Never compare boolean values for equality; gboolean is an int-sized
// type, which means anything that is not FALSE/0 will have a TRUE value
if (retval) {
g_print ("file added to recent files.\n");
} else {
g_warning ("there was a problem.\n");
}
g_free (uri);
// Start the loop; this will block until GtkRecentManager emits the
// "changed" signal, telling us the the recently used files list has
// been updated
g_main_loop_run (loop);
// Use standard exit codes
return retval ? EXIT_SUCCESS : EXIT_FAILURE;
}
Since you must call gtk_init(), you will need a connection to your display server; this means you cannot run this program under a pure virtual terminal outside of your graphical session. If you need to do that, you will need to write your own "recent manager", using the GBookmarkFile API, which is the same API GTK uses internally. The GBookmarkFile API is definitely more low level, though, and it requires you understand the file format and location used for the recently used files.
Related
I am writing a GTK+ frontend program that shreds files. The program shred from GNU Coreutils is the backend. The programming language is C.
So, I have a progress bar that show how much of the shredding is finished. But, in order to give the user accurate data on how much is shredded, the output of shred must be analyzed.
So far I have tried g_spawn_command_line_async, but I am not able to send the output of shred to a file.
The easiest solution is using unix redirection. You can call system("shred -v file_to_shred > somefile.txt") and the output will be redirect to the file. You can then read from the file using POSIX read or some other file reading method. Run the system() call in a seperate thread, and the just incremently check the file in the main thread. You could use POSIX sleep() to check the file at some interval, say, every half second. However, never use sleep on the GUI thread, as this will freeze the UI.
This is possible with GSubprocess (From GIO). Here is the simplified source code.
For this to work you will need gimp installed. (It displays the help text from the option --help)
#include <gtk/gtk.h>
struct everything
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *new_process;
GtkWidget *gimp_help_label;
};
void on_new_process_clicked (GtkWidget *wid, gpointer data)
{
struct everything *wids = data;
GError *error = NULL; /* This contains and error that MAY be generated by the gio functions. */
GInputStream *stdout_output;
GSubprocess *process;
gchar hello[10000];
process = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error, "gimp", "--help", NULL);
stdout_output = g_subprocess_get_stdout_pipe(process);
g_input_stream_read (stdout_output, hello, 9995, NULL, &error);
gtk_widget_hide(wids->new_process); /* Hide the button */
wids->gimp_help_label = gtk_label_new(hello); /* Make a new label. */
gtk_box_pack_start(GTK_BOX(wids->box), wids->gimp_help_label, TRUE, TRUE, 20); /* Add it to the box. */
gtk_widget_show(wids->gimp_help_label); /* Show it */
}
void exit_prog (GtkWidget *wid, gpointer data)
{
gtk_main_quit();
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
struct everything *widgets = (struct everything*)malloc(sizeof(struct everything));
widgets->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(widgets->window), 500, 500);
g_signal_connect(widgets->window, "destroy", G_CALLBACK(exit_prog), NULL);
widgets->box = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(widgets->window), widgets->box);
widgets->new_process = gtk_button_new_with_label("Show Gimp Help");
gtk_box_pack_start(GTK_BOX(widgets->box), widgets->new_process, TRUE, TRUE, 20);
g_signal_connect(widgets->new_process, "clicked", G_CALLBACK(on_new_process_clicked), widgets);
gtk_widget_show_all(widgets->window);
gtk_main();
return 0;
}
Thats how you do it.
Overview
I have been writing code to display menus and screens using the ncurses++ library. The desired result is to have these menus and screens output across a serial terminal interface.
Current Attempts
I can successfully do this using the base C ncurses library using calls.
if( (FDTERM = fopen("/dev/ttyS0", "r+")) != NULL )
{
if(FDTERM == NULL)
{
fprintf(stderr, "Error opening device: %s.\n", ncurses_device);
}
/* Set Screen */
MY_SCREEN = newterm(NULL, FDTERM, FDTERM);
if(MY_SCREEN != NULL)
{
/* Set the terminal */
set_term(MY_SCREEN);
}
}
and for getting this to work in c++ I wrote some intercept.c code to override what the call to ::initscr() in cursesw.cc actually calls to
#define ncurses_device "/dev/ttyS0"
NCURSES_EXPORT(WINDOW *) initscr(void)
{
WINDOW *result;
pthread_mutex_lock(&CUSTOM_LOCK);
if (!CUSTOM_INITIALIZED)
{
CUSTOM_INITIALIZED = true;
if( (FDTERM = fopen(ncurses_device, "r+")) != NULL )
{
if(FDTERM == NULL)
{
fprintf(stderr, "Error opening device: %s.\n", ncurses_device);
}
/* Set Screen */
MY_SCREEN = newterm(NULL, FDTERM, FDTERM);
if(MY_SCREEN != NULL)
{
/* Set the terminal */
set_term(MY_CDU_SCREEN);
}
/* def_shell_mode - done in newterm/_nc_setupscreen */
def_prog_mode();
}
else
{
CUSTOM_INITIALIZED = true;
NCURSES_CONST char *name;
if ((name = getenv("TERM")) == 0 || *name == '\0')
{
static char unknown_name[] = "unknown";
name = unknown_name;
}
if (newterm(name, stdout, stdin) == 0)
{
fprintf(stderr, "Error opening terminal: %s.\n", name);
result = NULL;
}
}
}
#if NCURSES_SP_FUNCS
#ifdef CURRENT_SCREEN
NCURSES_SP_NAME(def_prog_mode) (CURRENT_SCREEN);
#else
NCURSES_SP_NAME(def_prog_mode) (SP);
#endif
#else
def_prog_mode();
#endif
result = stdscr;
pthread_mutex_unlock(&CUSTOM_LOCK);
return Win(result);
}
The intercept.c allows the device as defined to be used if available. It
also allows initscr() to fall back to the default behavior of using the current terminal.
This works when used when for debugging, but I feel there has to be a better way to setup NCurses or the environment to direct NCurses output over the desired serial port.
The above solution now does not work when executing code on boot as there is not a terminal definition available.
This is being developed to support both RHEL 7 and 6. Some research seems to point to creating a new environment service using getty, agetty or by editing start-tty.conf as noted (https://unix.stackexchange.com/a/318647).
But the other issue is pointing NCurses++ to output to the correct environment. From what I have seen in NCurses source code it appears that the ::initscr() is called by default from cursesw.cc, which makes it harder to just point NCurses to a new environment.
Questions
How do I setup NCurses++ to output to a specified tty?
How to properly setup an environment for NCurses to use on system boot?
Update 1:
Updated code to do the following:
// Save the current stdin/stdout file descriptors
int saved_stdin = dup(fileno(stdin));
int saved_stdout = dup(fileno(stdout));
// Set the stdin/stdout to the desired device
freopen(ncurses_device, "w+", stdout);
freopen(ncurses_device, "r+", stdin);
// Initialize the NCursesMenu
NCursesMenu *m_pMenu = new NCursesMenu(m_pMenuItems);
// Restore the saved_stdin/stdout to the correct locations
dup2(saved_stdin, STDIN_FILENO);
dup2(saved_stdout, STDOUT_FILENO);
// Close the saved_stdin/stdout file descriptors
close(saved_stdin);
close(saved_stdout);
This allows NCurses to copy the current FILE* as defined here in newterm, but once the stdin/stdout FILE* is restored to the saved file descriptor the manipulation is lost. If the device is only pointed to the new device it works for NCurses, but all debug/test information is not visible because it is overwritten by NCurses in the current terminal.
initscr doesn't assume anything about the devices: it uses the current input/output stdin and stdout when initializing (and via newterm, setupterm, it copies the file-descriptors).
If your application (or environment/scripting) sets those streams to the device you'd like to connect to, that should be enough for the C++ interface. ncurses doesn't use the C+ cout, etc.
I'm having memory management related crashes when using SQLite. It only crashes once every 30 or so tries unless I enable Guard Malloc (a test mode) in Xcode, in which case it crashes the second time I prepare a statement, 100% of the time. I think it has to do with how I'm opening or using the database, but I can't find anything wrong, BUT I'm a newbie with SQLite. Is there anything I'm forgetting?
Wrapper function for opening:
int databaseConnect(sqlite3 **db){
int rc = sqlite3_open_v2(dbURL, db, SQLITE_OPEN_READWRITE, NULL);
if(rc!=SQLITE_OK){
fprintf(stderr, "Can't open database! Error: %s\n", sqlite3_errmsg(*db));
sqlite3_close_v2(*db);
return(SQL_ERROR);
}
return NO_ERROR;
}
Wrapper function for sending commands:
int databaseCommand(char* command, sqlite3* db){
char* error = NULL;
int ret = sqlite3_exec(db, command, NULL, 0, &error);
if (ret!=SQLITE_OK){
printf("SQL command aborted. Error: %s\n", error);
return SQL_ERROR; //EDIT: this will cause the database to close later
}
if (error) sqlite3_free(error);
return NO_ERROR;
}
How I use my opening function:
//ONCE IN MAIN THREAD, BEFORE ANY OTHER THREADS:
sqlite3* db = NULL;
databaseConnect(&db);
//call databaseCommmand a few times while creating tables...
sqlite3_close_v2(db);
//ONCE PER THREAD IN OTHER THREADS:
sqlite3* db = NULL; databaseConnect(&db);
How I use sqlite3_prepare_v2 in my non-main threads (and where it crashes):
struct LinkedList* databaseSelect(char* command, sqlite3* db){
sqlite3_stmt* stmt = NULL;
int retval = retval = sqlite3_prepare_v2(db,command,(strlen(command))*sizeof(char),&stmt,NULL); //crashes here the second time I run it
if(retval!=SQLITE_OK){
printf("Selecting data from database failed! Error: %s\n", sqlite3_errmsg(db));
sqlite3_free(stmt);
return NULL; //EDIT: this will cause the database to close later
}
// Then the function does stuff involving sqlite3_column_text and sqlite3_column_int…
sqlite3_free(stmt);
// return the linked list result
}
The error I get and the part of the SQLite3 library that causes it:
EXC_BAD_ACCESS (code=1) in this part of sqlite3.c:
/*
** Create a new virtual database engine.
*/
SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){
Vdbe *p;
p = sqlite3DbMallocZero(db, sizeof(Vdbe) );
if( p==0 ) return 0;
p->db = db;
if( db->pVdbe ){
db->pVdbe->pPrev = p; //error is right here; db->pVdbe is pointing to invalid address
}
p->pNext = db->pVdbe;
p->pPrev = 0;
db->pVdbe = p;
p->magic = VDBE_MAGIC_INIT;
return p;
}
Whenever I use sqlite3_column_text, I copy the result immediately. I do not modify the result. In databaseCommand and databaseSelect, char* command is null-terminated and valid (I checked). Each thread uses its own database handle, each connected to the same database. However, in this test case, there is only one thread connected to the database at any given time.
If there really is nothing wrong here, I have to assume that I trampled the memory elsewhere in my program, and I can't find anything in the rest of the program that even looks a bit dangerous. Plus it's suspicious that SQLite is the one thing crashing every time.
The sqlite3_prepare_v2 documentation says:
The calling procedure is responsible for deleting the compiled SQL statement using sqlite3_finalize() after it has finished with it.
sqlite3_free() can be used only for raw memory allocated with sqlite3_alloc(), or when a function such as sqlite3_exec() is documented as requiring it.
Since you are using multiple threads operating on same database just make sure you close and reopen the database from these thread after every operation. You should also try not to neglect the error condition and add the close statement there also as shown below.
if(retval!=SQLITE_OK){
printf("Selecting data from database failed! Error: %s\n", sqlite3_errmsg(db));
sqlite3_free(stmt);
sqlite3_close(your_db_ptr);
......
}
sqlite3_prepare_v2() alone just compiles the SQL but does not run it. Call sqlite3_step() on the compiled statement to run it, or use sqlite3_exec() that combines prepare+step+finalize into one function call. From here.
Hope this helps.
According to Microsoft this function should retrieve the name of the network resource associated with a local device.
I am using an adaptation of the code on this Microsoft page, compiling in C. But even though the drives ("J:", and "X:") are mapped on my PC, this function does not return the share for either of them. Rather it results in either ERROR_NOT_CONNECTED or ERROR_CONNECTION_UNAVAIL.
Note: As a double check, to eliminate the possibility that I was bumping up against UAC, or some other access problem, I called WNetGetConnection(,,) from within MS Excel using a macro with code from this site. It worked just fine.
My exact C code implementation is below.
Any ideas?
My adaptation using ANSI C, built and run on Windows 7, (must include mpr.lib)
#include <windows.h>
#include <ansi_c.h>
LPTSTR szDeviceName[260]; //MAX_PATHNAME_LEN
DWORD dwResult, cchBuff = sizeof(szDeviceName);
void main(int argc, char *argv[])
{
// Call the WNetGetConnection function.
dwResult = WNetGetConnection(argv[1],
(LPTSTR) szDeviceName,
&cchBuff);
switch (dwResult)
{
//
// Print the connection name or process errors.
//
case NO_ERROR:
printf("Connection name: %s\n", szDeviceName);
break;
//
// The device is not a redirected device.
//
case ERROR_NOT_CONNECTED:
printf("Device %s is not connected.\n", argv[1]);
break;
//
// The device is not currently connected, but it is a persistent connection.
//
case ERROR_CONNECTION_UNAVAIL:
printf("Connection to %s is unavailable.\n", argv[1]);
break;
//
// Handle the error.
//
default:
printf("WNetGetConnection failed.\n");
}
getchar();
}
I have a big program that is causing me access violation when using sdl_rwops. So I decided to create a small program just to test sdl_rwops and it is causing me the same access violation. For what I could see it is being caused because of the fopen_s() part but I could not figure out why.
Maybe you guys can find what I am missing.
Here is the code:
#include "SDL.h"
#include < stdio.h >
int main(int argc, char *argv[])
{
FILE *file;
SDL_Surface *screen;
SDL_Surface *one;
SDL_Rect rect;
errno_t err;
/* This is the RWops structure we'll be using */
SDL_RWops *rw;
SDL_Init(SDL_INIT_VIDEO);
atexit(SDL_Quit);
screen = SDL_SetVideoMode(640, 480, 16, SDL_DOUBLEBUF);
/* Here we get a file pointer to a bitmap */
if ( (err = fopen_s(&file,"penguin.bmp", "r")) != 0)
{
printf("Couldn't load penguin.bmp\n");
exit(1);
}
/* This is where we actually create our RWops structure. Here we're
instructing SDL to get it from the file pointer we just opened */
rw = SDL_RWFromFP(file, 0);
/* SDL_LoadBMP_RW is the RWops version of the old standby,
SDL_LoadBMP. This will get the image from the RWops you passed
in; in this case the file you've opened */
one = SDL_LoadBMP_RW(rw, 0); // This line calls fopen.c and causes the crash
/* Clean up after ourselves */
SDL_FreeRW(rw);
fclose(file);
/* Haphazard way of getting stuff to the screen */
rect.x = rect.y = 20;
rect.w = one -> w;
rect.y = one -> h;
SDL_BlitSurface(one, NULL, screen, &rect);
SDL_Flip(screen);
SDL_Delay(3000);
}
And here is the portion of fopen.c that causes the crash of the program:
errno_t __cdecl _tfopen_s (
FILE ** pfile,
const _TSCHAR *file,
const _TSCHAR *mode)
{
_VALIDATE_RETURN_ERRCODE((pfile != NULL), EINVAL);
*pfile = _tfsopen(file, mode, _SH_SECURE); // This line causes the access violation
if(*pfile != NULL)
return 0;
return errno;
}
The line
one = SDL_LoadBMP_RW(rw, 0);
jumps to fopen.c and the line
*pfile = _tfsopen(file, mode, _SH_SECURE);
in that file makes it crash.
I am using Visual Studio 2012 and the picture is in the same folder as the executable. The SDL.dll and even SDL_image.dll are there too.
I found a post in google of the same issue and the person said that when he put the whole path (instead of just penguin.bmp") it would not crash. It did not work for me, crashed anyway.
I am starting to think that it may be some SDL initialization issue but I did anything I could find on google and nothing worked. I tryed running in Multi-threaded DLL (/Md), in Multi-threaded Debug Dll (/MDd), tryed running in 64 bits, tyred changing the Subsystem to Windows and to console... everything resulted in the same crash.
If you read the documentation of SDL_RWFromFP
Description
SDL_RWFromFP creates a new SDL_RWops structure from a file pointer, opened with stdio. If autoclose is nonzero, the file will be automatically closed when the RWops structure is closed.
Note: This is not available under Win32, since files opened in an application on that platform cannot be used by a dynamically linked library.
Note: This function is not present in SDL-1.2-svn4446
So you need to use SDL_RWFromFile if you want to use RWOps.
Also SDL 2 has many improvements over SDL 1.* and I would highly recommend switching to that.