I followed by vulkan tutorial, and i get segfault on vkCreateInstance.
I use arch (btw), and i install vulkan by paru -S vulkan-devel (also linux-firmware and mesa are installed). My CPU is AMD Ryzen 7 2700, and GPU is Radeon RX 580 4GB.
I saw a similar question, but the answer did not solve my problem (and besides, they use C++, not C)
#include <stdio.h>
#include <stdlib.h>
#include <vulkan/vulkan.h>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
const char* APPLICATION_NAME = "Vulkan Teach";
const uint16_t WIDTH = 800;
const uint16_t HEIGHT = 600;
static GLFWwindow* window;
static VkInstance instance;
void error(char* message, int code) {
fprintf(stderr, "%s (status code: %d)\n", message, code);
exit(code);
}
void initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, APPLICATION_NAME, NULL, NULL);
}
void mainLoop() {
while(!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
}
void createInsance() {
VkApplicationInfo appInfo;
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = APPLICATION_NAME;
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "No Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
uint32_t glfwExtensionsCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionsCount);
createInfo.enabledExtensionCount = glfwExtensionsCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;
createInfo.enabledLayerCount = 0;
VkResult result = vkCreateInstance(&createInfo, NULL, &instance);
if(result != VK_SUCCESS) {
error("Failed to create vulkan instance.", result);
}
}
void initVulkan() {
createInsance();
}
void cleanup() {
vkDestroyInstance(instance, NULL);
glfwDestroyWindow(window);
glfwTerminate();
}
int main() {
initWindow();
initVulkan();
mainLoop();
cleanup();
return 0;
}
You are most probably passing uninitialized values to your instance creation. This part of the code:
VkInstanceCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
Only initializes the sType and pApplicationInfo members of the create info, while other important members such as pNext are uninitialized and as such may contain values that the driver doesn't know how to handle, resulting in the segfault.
So as a general rule for all (Vulkan) structs: Zero initialize like this:
VkInstanceCreateInfo createInfo = {0};
All the tutorials seem to indicate that I am doing things correctly, the vertex shader works, however it fails to recognize any input changes from the main program through the use of the glUniform1f function. I check glGetError after each line, there are no errors. I check glGetShaderiv and glGetShaderInfoLog, there are no issues. I am testing with OpenGL version 2.1 (unknown profile, but assuming the core profile) as reported by SDL.
#if defined(__WINDOWS__) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__)\
|| defined(__CYGWIN__)
/* Compiling for Windows */
#ifndef __WINDOWS__
#define __WINDOWS__
#endif
#include <windows.h>
#endif/* Predefined Windows macros */
#include <SDL2/SDL.h>
#include <GL/GL.h>
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
//return type not verified
void glGenBuffers();
void glBindBuffer();
void glBufferData();
unsigned int glCreateShader();
void glShaderSource();
void glCompileShader();
void glGetShaderiv();
void glGetShaderInfoLog();
unsigned int glCreateProgram();
void glAttachShader();
void glLinkProgram();
void glGetProgramiv();
void glGetProgramInfoLog();
void glVertexAttribPointer();
void glEnableVertexAttribArray();
void glUseProgram();
void glDeleteShader();
void glGenVertexArrays();
void glBindVertexArray();
GLint glGetUniformLocation();
void glUniform1f();
void glDeleteProgram();
void glDeleteBuffers();
int fixSDLconsole() {
FILE *console = freopen("stdout.txt", "a",stdout);
if (console == NULL) {return errno;}
console = freopen("stdout.txt", "a",stderr);
if (console == NULL) {return errno;}
return 0;
}
void printGLVersionNumber() {
int majorVersion;
int minorVersion;
int profile;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &majorVersion);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minorVersion);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile);
fprintf(stderr,"GL version %d.%d ",majorVersion,minorVersion);
switch (profile) {
case SDL_GL_CONTEXT_PROFILE_CORE: fprintf(stderr,"core (%d)\n",profile);break;
case SDL_GL_CONTEXT_PROFILE_COMPATIBILITY: fprintf(stderr,"compatibility (%d)\n",profile);break;
case SDL_GL_CONTEXT_PROFILE_ES: fprintf(stderr,"E.S. (%d)\n",profile);break;
default: fprintf(stderr, "unknown profile: %d\n",profile);break;
}
return;
}
#define checkGlError(label) {int error = glGetError();if (error != GL_NO_ERROR) {error_at_line(0,0,__FILE__,__LINE__,"error=%d", error);goto label;}}
int main(int argc, char **argv) {
SDL_Window *window = NULL;
SDL_GLContext context = NULL;
GLuint verticesGlIds[] = {0,0};
GLuint vertexShaderGlId = 0;
GLuint shaderProgramGlId = 0;
if (fixSDLconsole()) {
return errno;
}
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
error_at_line(1,0,__FILE__,__LINE__,"Unable to initialize SDL: %s",SDL_GetError());
goto error;
}
printGLVersionNumber();
window = SDL_CreateWindow("Window Title",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,640,SDL_WINDOW_OPENGL);
if (window == NULL) {
error_at_line(0,0,__FILE__,__LINE__,"Could not create window: %s", SDL_GetError());
goto error;
}
context = SDL_GL_CreateContext(window);
if (context == NULL) {
error_at_line(0,0,__FILE__,__LINE__,"Could not create OpenGL context: %s", SDL_GetError());
goto error;
}
glViewport(0,0,640,640);checkGlError(error);
glClearColor(.9f,.9f,.9f,1.f);checkGlError(error);
glEnableClientState(GL_VERTEX_ARRAY);checkGlError(error);
glEnableClientState(GL_COLOR_ARRAY);checkGlError(error);
float vertices[] = {
-.5f,0.f,0.f,
0.f,.5f,0.f,
0.f,-.5f,0.f,
0.f,.5f,0.f,
.5f,.5f,0.f,
0.f,0.f,0.f
};
float colors[] = {
1.f,0.f,0.f,//red
.5f,0.f,0.f,//red
0.f,1.f,0.f,//green
0.f,.5f,0.f,//green
0.f,0.f,1.f,//blue
0.f,0.f,.5f//blue
};
glGenBuffers(2, &verticesGlIds);checkGlError(error);
glBindBuffer(GL_ARRAY_BUFFER, verticesGlIds[0]);checkGlError(error);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);checkGlError(error);
glBindBuffer(GL_ARRAY_BUFFER, verticesGlIds[1]);checkGlError(error);
glBufferData(GL_ARRAY_BUFFER,sizeof(colors),colors, GL_STATIC_DRAW);checkGlError(error);
char *vertexShader =
"#version 120\n"\
"attribute vec3 aPos;\n"\
"uniform float i;\n"\
"void main() {\n"\
"gl_FrontColor=gl_Color;\n"\
"gl_Position = vec4(aPos.x+i/2,aPos.y,aPos.z,1.0);\n"\
"}\n";
vertexShaderGlId = glCreateShader(GL_VERTEX_SHADER);checkGlError(error);
if (vertexShaderGlId == 0) {error_at_line(0,0,__FILE__,__LINE__,"vertex shader could not be created");goto error;}
glShaderSource(vertexShaderGlId, 1, &vertexShader, NULL);checkGlError(error);
glCompileShader(vertexShaderGlId);checkGlError(error);
{
GLint success;
glGetShaderiv(vertexShaderGlId, GL_COMPILE_STATUS, &success);checkGlError(error);
if (success == GL_FALSE) {
char infoLog[512];
glGetShaderInfoLog(vertexShaderGlId, 512, NULL, infoLog);checkGlError(error);
error_at_line(0,0,__FILE__,__LINE__,"Vertex Shader problem: %s", infoLog);
goto error;
}
}
shaderProgramGlId = glCreateProgram();checkGlError(error);
if (shaderProgramGlId == 0) {error_at_line(0,0,__FILE__,__LINE__,"shader program could not be created");goto error;}
glAttachShader(shaderProgramGlId, vertexShaderGlId);checkGlError(error);
glLinkProgram(shaderProgramGlId);checkGlError(error);
{
int success;
glGetProgramiv(shaderProgramGlId, GL_LINK_STATUS, &success);checkGlError(error);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(shaderProgramGlId, 512, NULL, infoLog);checkGlError(error);
error_at_line(0,0,__FILE__,__LINE__,"Shader program problem: %s", infoLog);
}
}
glDeleteShader(vertexShaderGlId);checkGlError(error);
GLint iLocation = glGetUniformLocation(shaderProgramGlId, "i");checkGlError(error);
if (iLocation == -1) {error_at_line(0,0,__FILE__,__LINE__,"uniform i not found in shader");goto error;}
error_at_line(0,0,__FILE__,__LINE__,"iLocation: %d", iLocation);
for (int frame = 0; frame < 100; ++frame) {
glClear(GL_COLOR_BUFFER_BIT);checkGlError(error);
glUseProgram(shaderProgramGlId);checkGlError(error);
glBindBuffer(GL_ARRAY_BUFFER, verticesGlIds[0]); checkGlError(error);
glVertexPointer(3,GL_FLOAT,0,0); checkGlError(error);
glBindBuffer(GL_ARRAY_BUFFER, verticesGlIds[1]); checkGlError(error);
glColorPointer(3,GL_FLOAT,0,0); checkGlError(error);
glUniform1f(iLocation, (float) (frame%2)); checkGlError(error);
glDrawArrays(GL_TRIANGLES, 0,sizeof(vertices)/sizeof(float)/3); checkGlError(error);
glBindBuffer(GL_ARRAY_BUFFER, 0); checkGlError(error);
SDL_GL_SwapWindow(window);
SDL_Delay(100);
}
glDeleteProgram(shaderProgramGlId);
glDeleteShader(vertexShaderGlId);
glDeleteBuffers(sizeof(verticesGlIds)/sizeof(GLuint), verticesGlIds);
SDL_GL_DeleteContext(context);
SDL_Delay(3000);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
error:
glDeleteProgram(shaderProgramGlId);
glDeleteShader(vertexShaderGlId);
glDeleteBuffers(sizeof(verticesGlIds)/sizeof(GLuint), verticesGlIds);
if (context != NULL) SDL_GL_DeleteContext(context);
if (window != NULL) SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_FAILURE;
}
#if defined(__WINDOWS__)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
char *argv[1] = {(char *) 0};
return main(0, argv);
}
#endif
note that I am not familiar with OpenGL's extension function loading issues and routines such as SDL's SDL_GL_LoadLibrary and SDL_GL_GetProcAddress I just manually define the method signatures at the top of the file and import GL through the linker. I don't expect this to be an issue but it is the only issue, I am aware of, that I haven't looked into, that may be causing my problems.
So you declare the function like this:
void glUniform1f();
By omitting any parameters, the compiler will assume that all arguments are of type int. For most other GL functions, this works by chance, because those arguments are just integer types in most cases anyway, but for glUniform1f, it will mean that the function argument is converted to integer, but the resulting bit-pattern is implicitely re-interpreted as GLfloat by the function since the actual prototype for glUniform1f is something like this
void glUniform1f(int location, GLfloat value);
note that I am not familiar with OpenGL's extension function loading issues and routines such as SDL's SDL_GL_LoadLibrary and SDL_GL_GetProcAddress I just manually define the method signatures at the top of the file and import GL through the linker.
You shouldn't do this. The GL functions you try to access might not even be exported by the library at all. If you do not want to manually deal with loading every function pointer, you can use one of the existing OpenGL loaders.
gl_Position expects Clip-space coordinates, which are a hiper-cube of size [2w,2w,2w,w].
For vec4(x, y, z, w) if any of the [x,y,z] is outside of [-w,w] range, then the vertex is clipped.
The coordinates will be automatically converted by the GPU to NDC-space x/w, y/w, z/w, 1 (aka "perspective division") before the fragment shader.
Your GLSL code gl_Position = vec4(aPos.x+i/2,aPos.y,aPos.z,1.0); uses the uniform i.
You update it by glUniform1f(iLocation, (float) (frame%2));
First issue is frame%2. Only 0 or 1 is passed to the GPU. With your current vertices data, only two pairs of triangles should be drawn.
Second issue is that frame is a value 0 <= frame < 100. So, if you pass frame instead of frame%2, then for most values aPos.x + i/2 will fall outside the Clip space and you will see only the first two triangle-pairs, or parts of them.
I am currently working on a project where we have a C thread implementation for UNIX systems using pthreads. Now we want to be able to run this entire project on Windows as well, and I am translating all the threading for WIN32. Now I encountered a problem for which I could not come up with a decent solution.
I have the thrd_create() function:
static inline int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
Args* args = malloc(sizeof(Args));
args->arg = arg;
args->function = func;
*thr = CreateThread(NULL, 0, wrapper_function, (LPVOID) args, 0, NULL);
if (!*thr) {
free (args);
return thrd_error;
}
return thrd_success;
}
This function is supposed to create a new thread, and the user provides a start function. For convenience, I would like to leave the implementation that calls thrd_create() untouched if possible. For this reason, I created a wrapper_function:
static inline DWORD wrapper_function(LPVOID arg) {
Args * args;
args = (Args*) arg;
DWORD res = args->function(args->arg); //This does obviously not work
return res;
}
My question is: What DWORD should my wrapper function return? The function provided by the user for the pthread implementation has void return type, so I won't get any result from that. Any suggestions?
EDIT
Args looks like this:
struct Args {
void (*function)(void * aArg);
void* arg;
};
typedef struct Args Args;
According to manuals it is better to stick to a correct signature and use return value:
Windows
Pthreads
The other matter of concern would be the lifetime of args, I'd say the best way is for a caller to clean up, so they need to be tracked with your thread until it terminates.
An approximate API could be something along the lines of the following:
/* Your general error codes enumeration
* which should probably reside in a general
* header
*/
typedef enum {
OK = 0,
// Your application specific error codes
} error_t;
#ifdef _WIN32
#include <Windows.h>
typedef HANDLE thread_handle_t;
#else // assume pthreads
#include <pthread.h>
typedef pthread_t thread_handle_t;
#endif
typedef error_t(*entry_func_t)(void*);
typedef struct {
entry_func_t func;
void *args;
error_t _result;
thread_handle_t _handle;
} thread_t;
// returns OK(0) on success
// returns error code indicating a problem
error_t thread_create(thread_t *t);
An aproximate implementation would be:
#ifdef _WIN32
DWORD _win_entry_f(void *args) {
thread_t *t = args;
t->_result = t->func(t->args);
return 0; // Or some other Windows-specific value
}
error_t thread_create(thread_t *t) {
error_t err = OK;
if(!(t->_handle = ThreadCreate(NULL, 0, _win_entry_f, t, 0, NULL))) {
switch (GetLastError()) {
// Populate error with code
}
}
return err;
}
#else
void * _pthread_entry_f(void *args) {
thread_t *t = args;
t->_result = t->func(t->args);
return NULL; // Or some other pthreads specific value
}
error_t thread_create(thread_t *t, entry_func_t func, void *args) {
error_t err = OK;
switch(pthread_create(&t->_handle, NULL, _pthread_entry_f, t)) {
case 0: break;
// other cases populate err
}
return err;
}
#endif
Invokation would look somewhat like this.
error_t func(void* args) {
return OK;
}
.....................
thread_t t = { .func = func, .args = NULL };
thread_create(&t);
Obviously you'll need to implement your own cancelation, result collection, join, ...
So, I'm trying to make a Pebble app that generates a random string when you press a button. I'm pretty sure I have the Pebble code right, but I'm not sure what to do with this error:
/sdk2/[long stuff here]/ In function `_sbrk_r':
/home/[more long stuff]: undefined reference to `_sbrk'
collect2: error: ld returned 1 exit status
Waf: Leaving directory `/tmp/tmpX94xY7/build'
Build failed
And here's my code:
#include <pebble.h>
#include <stdlib.h>
#include <stdio.h>
Window *window;
TextLayer *text_layer;
char* one[] = {"string1", "stringone", "stringuno"};
char* two[] = {"string2", "stringtwo", "stringdos"};
char* three[] = {"string3", "stringthree", "stringtres"};
char* four[] = {"string4", "stringfour", "stringcuatro"};
int length1 = sizeof(one)/sizeof(*one);
int length2 = sizeof(two)/sizeof(*two);
int length3 = sizeof(three)/sizeof(*three);
int length4 = sizeof(four)/sizeof(*four);
char* gen()
{
char out[256];
sprintf(out, "%s, and then %s %s %s.", one[rand() % length1], two[rand() % length2], three[rand() % length3], four[rand() % length4]);
char* result = malloc(strlen(out) + 1);
strcpy(result, out);
return result;
}
static void select_click_handler(ClickRecognizerRef recognizer, void *context)
{
char* stringGen = gen();
text_layer_set_text(text_layer, stringGen);
free(stringGen);
}
static void click_config_provider(void *context)
{
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
window_single_click_subscribe(BUTTON_ID_UP, select_click_handler);
window_single_click_subscribe(BUTTON_ID_DOWN, select_click_handler);
}
static void window_load(Window *window)
{
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(window_layer);
text_layer = text_layer_create((GRect) { .origin = { 0, 72 }, .size = { bounds.size.w, bounds.size.h } });
text_layer_set_text(text_layer, "Press >>>");
text_layer_set_text_alignment(text_layer, GTextAlignmentCenter);
layer_add_child(window_layer, text_layer_get_layer(text_layer));
}
static void window_unload(Window *window)
{
text_layer_destroy(text_layer);
}
void handle_init(void)
{
window = window_create();
window_set_click_config_provider(window, click_config_provider);
window_set_window_handlers(window, (WindowHandlers) {
.load = window_load,
.unload = window_unload,
});
const bool animated = true;
window_stack_push(window, animated);
}
void handle_deinit(void)
{
text_layer_destroy(text_layer);
window_destroy(window);
}
int main(void)
{
handle_init();
app_event_loop();
handle_deinit();
}
I can't figure out why I'm getting that error. It's a simple application, I just have these little tweaks.
Thank you in advance for your help!
According to this (old) FAQ, that error happens when you try to use a C standard library function that hasn't been implemented in the SDK. If you look in the API reference, snprintf is available, but not sprintf. You can replace your call to sprintf in gen with something like
snprintf(out, 256, "%s, and then %s %s %s.", one[rand() % length1], two[rand() % length2], three[rand() % length3], four[rand() % length4]);
I just tried this out and it builds fine.
(As an aside, it may be a better a idea to declare out a global static buffer and just write over it each time, instead of constantly dynamically allocating memory.)
I am trying to create a flume-thrift client in c (c_glib) but have trouble creating the gobject that shall be sent to the server. I get the following error at the line in main.c:
`GLib-GObject-WARNING **: IA__g_object_new_valist: object class `ThriftFlumeEventType' has no property named `timestamp'`
The code in flume_types.h and flume_types.c is autogenerated from thrift. Tell me if you need to see more code. I am thankful for all the help I can get!
Parts of the code in flume_types.h:
struct _ThriftFlumeEvent
{
ThriftStruct parent;
/* public */
gint64 timestamp;
gboolean __isset_timestamp;
Priority priority;
gboolean __isset_priority;
GByteArray * body;
gboolean __isset_body;
gint64 nanos;
gboolean __isset_nanos;
gchar * host;
gboolean __isset_host;
GHashTable * fields;
gboolean __isset_fields;
};
typedef struct _ThriftFlumeEvent ThriftFlumeEvent;
GType thrift_flume_event_get_type (void);
#define TYPE_THRIFT_FLUME_EVENT (thrift_flume_event_get_type())
Parts of the code in flume_types.c:
void
thrift_flume_event_instance_init (ThriftFlumeEvent * object)
{
printf("thrift_flume_event_instance_init");
/* satisfy -Wall */
THRIFT_UNUSED_VAR (object);
object->timestamp = 0;
object->__isset_timestamp = FALSE;
object->__isset_priority = FALSE;
object->body = NULL;
object->__isset_body = FALSE;
object->nanos = 0;
object->__isset_nanos = FALSE;
object->host = NULL;
object->__isset_host = FALSE;
object->fields = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
object->__isset_fields = FALSE;
}
GType
thrift_flume_event_get_type (void)
{
static GType type = 0;
if (type == 0)
{
static const GTypeInfo type_info =
{
sizeof (ThriftFlumeEventClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) thrift_flume_event_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (ThriftFlumeEvent),
0, /* n_preallocs */
(GInstanceInitFunc) thrift_flume_event_instance_init,
NULL, /* value_table */
};
type = g_type_register_static (THRIFT_TYPE_STRUCT,
"ThriftFlumeEventType",
&type_info, 0);
type.timestamp;
}
return type;
}
Parts of the code in main.c:
gpointer eventObj = g_object_new(TYPE_THRIFT_FLUME_EVENT,
"timestamp", 0,
"__isset_timestamp", 0,
"priority", 0,
"__isset_priority", 0,
"body", 0,
"__isset_body", 0,
"nanos", 0,
"__isset_nanos", 0,
"fields", 0,
"__isset_fields", 0,
0);
This is changing in Thrift 0.9.2: Objects generated by the C (GLib) compiler to represent Thrift structs will now expose their members as GObject properties, obviating the risky (and poor) practice of modifying an object's instance structure directly.
Starting with 0.9.2 the preferred way to initialize a struct object will be essentially what the poster originally expected:
ThriftFlumeEvent *event =
g_object_new (TYPE_THRIFT_FLUME_EVENT,
"timestamp", (gint64)t_stamp.tv_sec * 1000,
"priority", priority,
...
NULL);
Note the __isset_ fields are managed by the object itself and should not be set directly.
This was the solution to the problem:
ThriftFlumeEvent *event = g_object_new(TYPE_THRIFT_FLUME_EVENT, 0);
event->timestamp = (gint64)t_stamp.tv_sec * 1000;
event->__isset_timestamp = TRUE;
event->priority = priority;
event->__isset_priority = TRUE;
...
GObject properties are NOT C struct members. You need to install them via g_object_install_property in your class init function https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html . You also need to derive object from GObject (or from any struct that is derived from a GObject).
Note: derived in C means, that it has to have the parent as its first member so it can be cast seamlessly (NOT a pointer to a GObject!)
You should read a book on the topic, it can get quite complex.