changing value of int defined in struct - c

I defined a struct Board with an int n, the value of which is a small integer. Before this method is called by the gtk signal "clicked", board->n has the correct value.
However, when calling this funciton, the value of n printed out by the first printf statement is something really large 32665.
draw_token (GtkButton *button, cairo_t *cr, Board *board){
printf("n: %d\n", board->n);
printf("button clicked\n");
//parse label of a button into the corresponding column number
guint col = (int)strtol(gtk_button_get_label(button), NULL, 0);
if (make_move(board, col) == false){
printf("draw cairo\n");
}
}
the struct:
typedef struct board Board;
struct board{
int k;
int n;
char *moves;
int player;
};
function where the callback is located:
void gui_make_buttons(GtkWidget *box, Board *board){
guint n = board->n;
for (int i = 1 ; i <= n ; i++){
GtkWidget *button = gtk_button_new();
//make label for button
char label[3];
sprintf(label, "%d", i-1);
gtk_button_set_label((GtkButton*)button,label);
// gtk_widget_show(button);
gtk_container_add(GTK_CONTAINER(box), button);
g_signal_connect(button, "clicked",G_CALLBACK(gui_draw_token), board);
}
}
Could someone please explain to me why the value of n has changed to large values like 36751 whenever the button is clicked?
Thank you very much
complete code with main
gui.c:
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <stdbool.h>
#include "gui.h"
#include "board.h"
#include "connect.h"
gboolean draw_board (GtkWidget *widget, cairo_t *cr, gpointer board){
guint width, height;
width = gtk_widget_get_allocated_width (widget);
height = gtk_widget_get_allocated_height (widget);
guint row = height/((Board*)board)->n;
guint col = width/((Board*)board)->n;
printf("%d\n", row);
printf("%d\n", col);
// cairo_set_source_rgb (cr, 100, 100, 255);
// cairo_paint (cr);
cairo_set_source_rgb(cr, 0, 0, 255);
for (int i = 1; i < ((Board*)board)->n ; i++){
//draw horizontal grids;
cairo_move_to (cr, 0, row*i);
cairo_line_to (cr, width, row*i);
//draw vertical grids;
cairo_move_to (cr, col*i, 0);
cairo_line_to (cr, col*i, height);
}
// cairo_arc (cr, 100, 100, 50, 0, 2 * G_PI);
// cairo_move_to (cr, 30, 30);
// cairo_line_to (cr, 50, 50);
cairo_stroke (cr);
return false;
}
int main (int argc, char *argv[]){
//check for correct number of arguments.
if (!check_argument(argc, argv))
return EXIT_FAILURE;
int k = strtol(argv[1], NULL, 0);
int n = strtol(argv[2], NULL, 0);
play_game(k, n);
return EXIT_SUCCESS;
}
//show widgets and get gtk going
CGUI *gui_start_gtk(Board *board){
//assigns board to the gui struct at beginning of game
CGUI *gui = make_gui (board);
// gtk_widget_queue_draw (gui->drawing_area);
// gui_draw_init_board(gui);
gtk_widget_show_all (gui->window);
gtk_main ();
return gui;
}
/*
void gui_draw_init_board(GtkWidget *widget, cairo_t *cr, CGUI *gui){
printf("HI\n");
if (gui) {
guint k = gui->board->k;
guint n = gui->board->n;
printf("%d\n", k);
}
}
*/
void gui_make_buttons(GtkWidget *box, Board *board){
// guint n = board->n;
for (int i = 1 ; i <= (board->n) ; i++){
GtkWidget *button = gtk_button_new();
//make label for button
char label[3];
// sprintf(label, "%d", i-1);
sprintf(label, "%d", i-1);
gtk_button_set_label((GtkButton*)button,label);
// gtk_widget_show(button);
gtk_container_add(GTK_CONTAINER(box), button);
g_signal_connect(button, "clicked",G_CALLBACK(gui_draw_token), board);
}
}
void gui_draw_token (GtkButton *button, cairo_t *cr, gpointer board){
printf("button clicked\n");
printf("n: %d\n", ((Board*)board)->n);
//parse label of a button into the corresponding column number
guint col = (int)strtol(gtk_button_get_label(button), NULL, 0);
printf("%d\n", col);
printf("n: %d\n", ((Board*)board)->n);
if (make_move(board, col) == false){
printf("draw cairo\n");
}
}
CGUI *make_gui(Board *board){
CGUI *gui = (CGUI*) malloc(sizeof(CGUI));
//assign variables to gui object
gui->board = board;
GtkWidget *window;
GtkWidget *frame;
GtkWidget *drawing_area;
gtk_init(NULL, NULL);
//set up initial window
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Connect-N-K");
gtk_window_set_default_size (GTK_WINDOW(window), 600, 650);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show(window);
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), gui);
// g_signal_connect (window, "draw", G_CALLBACK (gui_draw_buttons), board);
//create boxes to fit buttons and drawing area
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
GtkWidget *draw_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
//make all buttons take up even amount of space
gtk_box_set_homogeneous((GtkBox*)button_box, true);
gtk_widget_set_size_request(button_box, 600, 50);
// gtk_box_pack_start (box, window, false, false, false);
gtk_container_add(GTK_CONTAINER(window), box);
gtk_container_add(GTK_CONTAINER(box), draw_box);
gtk_container_add(GTK_CONTAINER(box), button_box);
//set up initial frame
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_widget_set_size_request(frame, 600, 600);
gtk_container_add(GTK_CONTAINER(draw_box), frame);
//create and pack buttons.
gui_make_buttons(button_box, board);
//set up drawing area
drawing_area = gtk_drawing_area_new ();
gtk_widget_set_size_request(drawing_area, 600, 600);
gtk_container_add (GTK_CONTAINER (frame), drawing_area);
g_signal_connect (drawing_area, "draw", G_CALLBACK (draw_board), board);
printf("n: %d\n", board->n);
// printf("board in gui: %d\n", *board);
gui->window = window;
gui->frame = frame;
gui->drawing_area = drawing_area;
/*
//show widgets
connect.c :
/* connect.c */
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "gui.h"
#include "board.h"
#include "connect.h"
#define BUFFER_SIZE 10
/*
static int check_argument(int argc, char *argv[]);
static void play_game(int k, int n);
*/
/*
int main(int argc, char *argv[]){
//check for correct number of arguments.
if (!check_argument(argc, argv))
return EXIT_FAILURE;
int k = strtol(argv[1], NULL, 0);
int n = strtol(argv[2], NULL, 0);
play_game(k, n);
return EXIT_SUCCESS;
}
*/
int check_argument(int argc, char *argv[]){
if(argc < 3 || argc >3){
fprintf(stderr, "Parameters entered incorrectly. Input two integers for k and n respectively.\n");
return false;
}
else
return true;
}
//go through all steps of the game
void play_game(int k, int n){
//check to see if k and n are appropriate
if(k>n){
fprintf(stderr, "k is greater than n, game will never be won.\n");
return;
}
Board *board = make_board(k, n);
// print_board(board);//print initial board
//initiate gui
// CGUI *gui = make_gui();//set first three vars
// gui_set_board(gui, board);//set the fourth var
// CGUI *gui = gui_start_gtk(board);
// connect_play_game_text(board);
connect_play_game_gui(board);
/* int player = 1; //let first player go first
char *s = (char*)malloc(BUFFER_SIZE);//allocate memory for int to read
int move_result;
do{
fgets(s, BUFFER_SIZE, stdin);
int cols = strtol(s, NULL, 0);
move_result = make_move(board,cols,player);
//switch players if legal move and no one wins
if(move_result == false)
player = 3-player;
//do nothing is move is illegal(move_result = -1, thus letting
//the same player choose again.
}
while(move_result != true);
//free up resources
free(s);
destroy_board(board);
*/
}
int connect_play_game_gui(Board *board){
printf("n in connect: %d\n", board->n);
CGUI *gui = gui_start_gtk(board);
// gui_set_board(gui, board);//set the fourth var
// int player = 1; //let first player go first
// char *s = (char*)malloc(BUFFER_SIZE);//allocate memory for int to read
// int move_result;
// fgets(s, BUFFER_SIZE, stdin);
// int cols = strtol(s, NULL, 0);
// move_result = make_move(board,cols,player);
//switch players if legal move and no one wins
// if(move_result == false){
// player = 3-player;
return true;
// }
//do nothing if move is illegal(move_result = -1, thus letting
//the same player choose again.
// while(move_result != true);
//free up resources
// free(s);
// destroy_board(board);
}
int connect_make_move_gui(int col, Board *board, int player){
return 1;
}
void connect_play_game_text(Board *board){
print_board(board);//print initial board
char *s = (char*)malloc(BUFFER_SIZE);//allocate memory for int to read
int move_result;
do{
fgets(s, BUFFER_SIZE, stdin);
int cols = strtol(s, NULL, 0);
move_result = make_move(board,cols);
}
while(move_result != true);
//free up resources
free(s);
destroy_board(board);
}
//initiate gui
//static void connect_init_gui
board.c:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "board.h"
#define DRAW 2
#define ILLEGAL_MOVE -1
static char set_player_char(int player);
static int check_draw(Board *board);
//make a new board
Board *make_board(int k, int n){
Board *board = (Board *) malloc(sizeof(Board));
board->moves = (char *)malloc(n*n);
(board->k) = k;
(board->n) = n;
board->player = 1;
//make an array of empty slots based of size n^2
for(int i = 0; i < n*n; i++){
board->moves[i] = '.';
}
return board;
}
//print board with updated moves, print last row first.
void print_board(Board *board){
int n = board->n;
//loop through each row
for(int i = n-1; i >= 0; i--){
//loop through each column
for(int j = 0; j<n; j++){
printf("%c", (board->moves)[i*n+j]);
printf(" ");//add space between columns
}
printf("\n"); //wrap around each row
}
printf("\n");
}
//set char for player
static char set_player_char(int player){
char player_char;
if (player == 1)
player_char = 'o';
else
player_char = '*';
return player_char;
}
//update board based on player's input, return the row move is made
int make_move(Board *board, int x){
printf("inmakemove n: %d\n", board->n);
// printf("board in make_move: %d\n", &board);
//if move is illegal, return
if (!check_legal_move(board, x))
return ILLEGAL_MOVE;
int n = board->n;
int row;
//loop through the rows of the given column to find an empty spot.
for (int i = 0; i < n; i++){
if ((board->moves)[i*n+x] == '.'){
(board->moves)[i*n+x] = set_player_char(board->player);
row = i;
break;
}
}
print_board(board);
//Check to see if a player has won the game.;
int stat = check_win(board, x, row);
if (stat == true){
fprintf(stdout, "Player %d won the game.\n", board->player);
return true;
}
//if all slots are filled, game is a draw.
if(stat == DRAW){
fprintf(stdout, "Game was a draw.\n");
return true;
}
//if no one won, game continues.
else{
board->player = 3-(board->player);
return false;
}
}
//check to see if move x is legal
int check_legal_move(Board *board, int x){
int n = board->n;
//see if column entered is legal.
if (x >= (board->n) || x<0){
fprintf(stderr, "Illegal move by player %d at column %d\
, number entered outside range of available columns.\n", board->player, x);
return false;
}
//see if column entered is already filled
if ((board->moves)[(n-1)*n+x] != '.'){
fprintf(stderr, "Illegal move by player %d at column %d\
, column is already filled.\n", board->player, x);
return false;
}
return true;
}
//check for winning move
int check_win(Board* board, int x, int row){
int n = board->n;
int k = board->k;
int current_move = row*n+x; //slot that the current move fills
char *moves = board->moves;
char player_char = set_player_char(board->player);
int score;
score = 1;
//Check to see how many continuous slots are filled with the current player'
//s token horizontally.
//going right
for (int i = 1; i<k && x+i<n; i++){
if(moves[current_move+i] == player_char)
score ++;
else
break;
}
//going left
for(int i = 1; i<k && x-i>=0; i++){
if(moves[current_move-i] == player_char)
score++;
else
break;
}
//if horizontally connect to k, the player wins.
if (score>=k)
return true;
//if not, check vertical.
score = 1;
//going up
for (int i = 1; i<k && row+i<n; i++){
if(moves[current_move+n*i] == player_char)
score ++;
else
break;
}
//going down
for(int i = 1; i<k && row-i>=0; i++){
if(moves[current_move-n*i] == player_char)
score ++;
else
break;
}
//if vertically connect to k, the player wins.
if (score>=k)
return true;
//if not, check rising to right diagnol. Reset score like previously.
score = 1;
//going right and up
for(int i = 1; i<k && row+i<n && x+i<n; i++){
if(moves[current_move+n*i+i] == player_char)
score ++;
else
break;
}
//going left and down
for(int i = 1; i<k && row-i>=0 && x-i>=0; i++){
if(moves[current_move-n*i-i] == player_char)
score ++;
else
break;
}
//if right diagonally connect to k, the player wins.
if (score>=k)
return true;
//if not, check left rising diagonal.
score = 1;
//check right and down
for(int i = 1; i<k && x+i<n && row-i>=0; i++){
if(moves[current_move-n*i+i] == player_char)
score ++;
else
break;
}
//check left and up
for(int i = 1; i<k && x-i>=0 && row+i<n; i++){
if(moves[current_move+n*i-i] == player_char)
score ++;
else
break;
}
//if left diagonally connect to k, the player wins.
if (score>=k)
return true;
if(check_draw(board))
return DRAW;
//if no k connect is made in any direction, game is not won.
return false;
}
//check to see if game has come to a draw
static int check_draw(Board *board){
int n = board->n;
//loop through the top row to see if there are any empty slots left
for (int i = 0; i<n; i++){
if((board->moves)[n*(n-1)+i] == '.')
return false;
}
//if no empty slots left, game was a draw.
return true;
}
//Free up resources.
void destroy_board(Board *board){
free(board->moves);
free(board);
}
I'm sorry this is long and messy, I'm still working on the code and new to C.

You need to change void gui_draw_token (GtkButton *button, cairo_t *cr, gpointer board) to void gui_draw_token (GtkButton *button, gpointer board)
You can't just make up callback signatures and hope things work. For signatures you need to look at the documentation appropriate to the signal. For example in this case the GtkButton clicked signal documentation.
It would also help us to answer your question if you would remove all the unnecessary code and commented out code.

not that I know gtk, but a quick google, it seems, the board you are getting in your callback isn't actually a board you are passing. Your callback signature for the clicked event is incorrect
it should look a little like
void button_clicked(GtkWidget *widget, gpointer data)

EDIT: Yeah don't pay attention to this. I thought the char label[3] was initialized outside the loop and he was updating the label[0] three times, resulting in undefined output for label[1] and label[2]
That's what I get for a cursory look at 739782781 lines of code. Sorry about that everyone.
you're not telling the label which one to update in the sprintf
char label[3];
sprintf(label, "%d", i-1);
^ is wrong
it should be
char label[3];
sprintf(label[n-1], "%d", i-1);
I think.

Related

How do I center widgets in a RowColumn with horizontal orientation in X11/Motif?

I am writing a replacement for xmmix that can switch between the different audio devices.
I would like to know how could I center the widgets. As seen in the image, they are added to the left. In this example, each audio slider control is Managed inside a RowColumn widget manager (see CREATE SLIDE WIDGET CONTAINER in draw_scale()) and each slider control is added to a horizontal RowColumn widget manager (see SLIDES COLUMN in main()). This big container for all the sliders has black background to easily notice that it takes all the width of the window but the inner RowColumns are drawn always on the left. I would like them to be centered.
Also I was expecting each slider container to take all the available height of the outer container because of the XmNbottomAttachment, XmATTACH_FORM, but that is not working.
#include <Xm/Xm.h>
#include <Xm/PushB.h>
#include <Xm/MainW.h>
#include <Xm/Label.h>
#include <Xm/Form.h>
#include <Xm/Scale.h>
#include <Xm/RowColumn.h>
#include <stdio.h>
#include <stdlib.h>
#include "devices.h"
struct button_data {
int device_num;
Widget button;
};
struct scale_data {
int scale_num;
Widget slide_widget;
Widget label;
Widget scale_container;
Widget scale_left;
Widget scale_right;
Widget lock;
};
/* GLOBAL VARIABLES */
int num_devices;
struct device *devices;
int num_channels;
struct channel *channels;
struct button_data *buttons;
struct scale_data *scales;
void button_callback(Widget widget, XtPointer client_data, XtPointer call_data);
void create_device_buttons(Widget parent, struct device *devices, int num_devices);
void create_channel_scales(Widget parent, struct channel *channels, int num_channels);
void set_rc_background(Widget rc);
void draw_scale(Widget parent, struct scale_data *s, struct channel *c);
void to_upper(char *str);
void to_upper(char *str) {
int j=0;
while (str[j]) {
str[j] = toupper(str[j]);
j++;
}
}
void draw_scale(Widget parent, struct scale_data *s, struct channel *c) {
Arg args[10];
int n;
char widget_name_slide[32];
char widget_name_slide_label[32];
char widget_name_scale_container[32];
char widget_name_scale_left[32];
char widget_name_scale_right[32];
char widget_name_slide_lock[32];
sprintf(widget_name_slide, "slide_%02d", c->number);
sprintf(widget_name_slide_label, "slide_label_%02d", c->number);
sprintf(widget_name_scale_container, "scale_container_%02d", c->number);
sprintf(widget_name_scale_left, "scale_left_%02d", c->number);
sprintf(widget_name_scale_right, "scale_right_%02d", c->number);
sprintf(widget_name_slide_lock, "slide_lock_%02d", c->number);
/* CREATE SLIDE WIDGET CONTAINER */
n = 0;
XtSetArg(args[n], XmNorientation, XmVERTICAL); n++;
XtSetArg(args[n], XmNentryAlignment, XmALIGNMENT_CENTER); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); n++;
s->slide_widget = XmCreateRowColumn(parent, widget_name_slide, args, n);
XtManageChild(s->slide_widget);
/* CREATE SLIDE LABEL */
XmString label;
to_upper(c->description);
label = XmStringCreateLocalized(c->description);
n = 0;
XtSetArg(args[n], XmNlabelString, label); n++;
s->label = XmCreateLabel(s->slide_widget, widget_name_slide_label, args, n);
XmStringFree(label);
XtManageChild(s->label);
/* CREATE SCALE CONTAINER */
n = 0;
XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); n++;
s->scale_container = XmCreateRowColumn(s->slide_widget, widget_name_scale_container, args, n);
XtManageChild(s->scale_container);
/* CREATE SCALE LEFT */
n = 0;
XtSetArg(args[n], XmNshowValue, False); n++;
XtSetArg(args[n], XmNvalue, c->value_left); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNrightWidget, s->scale_right); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
s->scale_left = XmCreateScale(s->scale_container, widget_name_scale_left, args, n);
XtManageChild(s->scale_left);
/* CREATE SCALE RIGHT */
n = 0;
XtSetArg(args[n], XmNshowValue, False); n++;
XtSetArg(args[n], XmNvalue, c->value_right); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, s->scale_left); n++;
s->scale_right = XmCreateScale(s->scale_container, widget_name_scale_right, args, n);
XtManageChild(s->scale_right);
}
void create_channel_scales(Widget parent, struct channel *channels, int num_channels) {
int i;
Arg args[5];
int n;
scales = malloc(num_channels * sizeof(struct scale_data));
if (scales == NULL) {
perror("Failed to allocate memory");
exit(EXIT_FAILURE);
}
/* CREATE CHANNEL SLIDERS */
for (i = 0; i < num_channels; i++) {
scales[i].scale_num = channels[i].number;
draw_scale(parent, &(scales[i]), &(channels[i]));
}
}
void button_callback(Widget widget, XtPointer client_data, XtPointer call_data) {
int i;
struct button_data *data = (struct button_data *)client_data;
int device_num = data->device_num;
// unclick all other buttons
for (i = 0; i < num_devices; i++) {
if (buttons[i].button != widget) {
XtVaSetValues(buttons[i].button, XmNset, False, NULL);
XmUpdateDisplay(buttons[i].button);
}
}
// click this button
XtVaSetValues(widget, XmNset, True, NULL);
XmUpdateDisplay(widget);
// set audio device
set_audio_device(device_num);
}
void create_device_buttons(Widget parent, struct device *devices, int num_devices) {
int i;
XmString label;
Arg args[5];
int n;
buttons = malloc(num_devices * sizeof(struct button_data));
if (buttons == NULL) {
perror("Failed to allocate memory");
exit(EXIT_FAILURE);
}
/* CREATE DEVICE SELECTOR */
for (i = 0; i < num_devices; i++) {
// create button
n = 0;
label = XmStringCreateLocalized(devices[i].description);
XtSetArg(args[n], XmNlabelString, label); n++;
buttons[i].button = XmCreatePushButton(parent, "device_button", args, n);
XmStringFree(label);
XtManageChild(buttons[i].button);
// set button data
buttons[i].device_num = devices[i].number;
// set button callback
XtAddCallback(buttons[i].button, XmNactivateCallback, button_callback, &buttons[i]);
}
}
int main(int argc, char *argv[]) {
XtAppContext app;
Widget top_level, main_window, button_rowcolumn, slides_rowcolumn;
Widget scales_pane, devices_pane;
int default_device;
int i;
int n=0;
Arg args[10];
//top_level = XtVaOpenApplication(&app, "XAudio", NULL, 0, &argc, argv, NULL, sessionShellWidgetClass, NULL);
top_level = XtVaAppInitialize(&app, "XAudio", NULL, 0, &argc, argv, NULL, NULL);
fprintf(stderr, "DEBUG 10\n");
/* CONTAINER FORM */
n=0;
XtSetArg(args[n], XmNresizable, True); n++;
XtSetArg(args[n], XmNresizePolicy, XmRESIZE_GROW); n++;
main_window = XmCreateForm(top_level, "main_window", args, n);
XtManageChild(main_window);
/* BUTTON ROW */
n=0;
XtSetArg(args[n], XmNorientation, XmVERTICAL); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
button_rowcolumn = XmCreateRowColumn(main_window, "button_rowcolumn", args, n);
XtManageChild(button_rowcolumn);
/* SLIDES COLUMN */
n=0;
XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, button_rowcolumn); n++;
slides_rowcolumn = XmCreateRowColumn(main_window, "slides_rowcolumn", args, n);
XtManageChild(slides_rowcolumn);
set_rc_background(slides_rowcolumn);
/* GET LIST OF DEVICES AND DEFAULT DEVICE */
default_device = get_default_device(&devices, &num_devices);
for(i=0; i<num_devices; i++) {
printf("%d %s\n", devices[i].number, devices[i].description);
}
/* GET LIST OF CHANNELS */
get_mixer_channels(&channels, &num_channels);
printf("num_channels %d\n", num_channels);
for(i=0; i<num_channels; i++) {
printf("%d %s [%03d-%03d]\n", i, channels[i].description, channels[i].value_left, channels[i].value_right);
}
create_device_buttons(button_rowcolumn, devices, num_devices);
create_channel_scales(slides_rowcolumn, channels, num_channels);
XtRealizeWidget(top_level);
/* CLICK DEFAULT BUTTON */
if (default_device >= 0) {
XtVaSetValues(buttons[default_device].button, XmNset, True, NULL);
XmUpdateDisplay(buttons[default_device].button);
}
XtAppMainLoop(app);
free(devices);
free(buttons);
return 0;
}

realloc causes "double free or corruption (fasttop)" error

I am programming a terminal snake game in C. User input is handled using a pthread which reads the direction pointer. To account for the direction of the snake, a direction vector, allocated dynamically, is increased in size each time the snake eats a fruit. For that, I created an append() function which takes the vector pointer as a argument, the current size of the snake and the value to be appended (last direction).
The game runs normally until the current size is 5 and then it aborts with the "double free or corruption" error. I have absolutely no idea why that happens. Any hints or ideas will be appreciated.
Here is the full code:
// a simple snake game which uses pthread.h library to handle user input
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <termios.h> // for getch
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#define SIZE_X 20
#define SIZE_Y 20
#define TIME 200 // game update interval (milliseconds)
bool collision;
typedef struct position {
int x;
int y;
} position;
int *append(int *v, int size, int value) // append data to vector
{
size = size + 1;
puts("Reallocing memory ...");
printf("\nCurrent size: %d\n", size);
v = (int*)realloc(v, (size)*sizeof(int*));
puts("Memory reallocating sucessful!");
v[size-1] = value;
}
int msleep(long msec) // sleep for the requested number of milliseconds
{
struct timespec ts;
int res;
if (msec < 0)
{
errno = EINVAL;
return -1;
}
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
do {
res = nanosleep(&ts, &ts);
} while (res && errno == EINTR);
return res;
}
char getch(void) // get char
{
char buf = 0;
struct termios old = {0};
fflush(stdout);
if(tcgetattr(0, &old) < 0)
perror("tcsetattr()");
old.c_lflag &= ~ICANON;
old.c_lflag &= ~ECHO;
old.c_cc[VMIN] = 1;
old.c_cc[VTIME] = 0;
if(tcsetattr(0, TCSANOW, &old) < 0)
perror("tcsetattr ICANON");
if(read(0, &buf, 1) < 0)
perror("read()");
old.c_lflag |= ICANON;
old.c_lflag |= ECHO;
if(tcsetattr(0, TCSADRAIN, &old) < 0)
perror("tcsetattr ~ICANON");
printf("%c\n", buf);
return buf;
}
int randomInt(int b, int a) // generates random integer between b and a
{
return ((rand() % (b+1 - a)) + a);
}
void *readMove(void *data) // argument to p_thread to read user input
{
int *direction = ((int*) data); // pass function void pointer to direction pointer
pthread_detach(pthread_self());
while (collision) {
if (getch() == '\033') { // if the first value is esc
fputs("\033[A\033[2K",stdout);
rewind(stdout);
getch(); // skip the [
fputs("\033[A\033[2K",stdout);
rewind(stdout);
switch(getch()) { // the real value
case 'A':
// code for arrow up
fputs("\033[A\033[2K",stdout);
rewind(stdout);
*direction = 0;
break;
case 'B':
// code for arrow down
fputs("\033[A\033[2K",stdout);
rewind(stdout);
*direction = 2;
break;
case 'C':
// code for arrow right
fputs("\033[A\033[2K",stdout);
rewind(stdout);
*direction = 1;
break;
case 'D':
// code for arrow left
fputs("\033[A\033[2K",stdout);
rewind(stdout);
*direction = 3;
break;
}
}
}
pthread_exit(NULL);
}
void beginGame() // welcome screen
{
int i;
for (i = 0; i < 4; i++) {
printf("WELCOME TO C-SNAKE! - Initializing game in %d seconds ...\n", (3-i));
sleep(1);
fputs("\033[A\033[2K",stdout);
rewind(stdout);
}
}
void clearScreen() // clear game screen
{
int i;
for (i=0; i <= SIZE_Y; i++) {
fputs("\033[A\033[2K",stdout);
rewind(stdout);
}
}
void printGame(char game[SIZE_X][SIZE_Y]) // print current game
{
int i, j;
printf("\r");
for (i=0; i<SIZE_X; i++) {
printf("\n");
for (j=0; j<SIZE_Y; j++) {
printf("%c ", game[i][j]);
}
}
printf("\n");
}
void initializeGame(char game[SIZE_X][SIZE_Y], position *player_position, int *direction) // initializes variables
{
int i, j;
// initializes movement direction - 0: up, 1: right, 2: down, 3: left
*direction = 2;
// initializes player position
player_position->x = 1;
player_position->y = 1;
// initializes board
for (i=0; i<SIZE_X; i++) {
for (j=0; j<SIZE_Y; j++) {
if ((i == player_position->x) && (j == player_position->y)) {
game[i][j] = 'o';
}
else {
game[i][j] = '_';
}
}
}
}
bool checkCollision(position *player_position) // checks for collision (game over)
{
if (player_position->x == 0) {
return false;
}
else if (player_position->y == 0) {
return false;
}
else if (player_position->x == (SIZE_X-1)) {
return false;
}
else if (player_position->y == (SIZE_Y-1)) {
return false;
}
else {
return true;
}
}
void updateGame(char game[SIZE_X][SIZE_Y], position *player_position, int *direction, int it, int *length) // updates snake position
{
int i, j, x_inc=0, y_inc=0;
switch (direction[it])
{
case 0:
x_inc = -1;
break;
case 1:
y_inc = 1;
break;
case 2:
x_inc = 1;
break;
case 3:
y_inc = -1;
break;
}
player_position->x = player_position->x+x_inc;
player_position->y = player_position->y+y_inc;
for (i=0; i<SIZE_X; i++) {
for (j=0; j<SIZE_Y; j++) {
if ((i == player_position->x) && (j == player_position->y)) {
if (game[i][j] == '+') {
*length = *length+1;
append(direction, *length, direction[it]);
}
game[i][j] = 'o';
}
else if (game[i][j] != '+') {
game[i][j] = '_';
}
}
}
}
void generateFruit(char game[SIZE_X][SIZE_Y]) // generate random fruits
{
position fruit_position;
while (true) {
fruit_position.x = randomInt(SIZE_X-2, 1);
fruit_position.y = randomInt(SIZE_Y-2, 1);
if (game[fruit_position.x][fruit_position.y] == '_') {
game[fruit_position.x][fruit_position.y] = '+';
break;
}
}
}
int main(int argc, char *argv[])
{
char game[SIZE_X][SIZE_Y]; // game tiles
int i=0; // auxiliary variable
int length = 0; // initial length
int current_length;
int counter = 0; // time counter
float total_time; // total game time
int fruit_interval = 10; // time interval (seconds) to random fruit generation
position player_position; // player head position (x,y)
int *direction = (int*)malloc(1*sizeof(int*)); // moving direction
int rc;
pthread_t pthread; // thread to handle player input
srand(time(NULL)); // uses time to generate random seed
beginGame(); // intro screen
initializeGame(game, &player_position, direction); // initializes variables
collision = checkCollision(&player_position);
rc = pthread_create(&pthread, NULL, readMove, (void *)direction);
if (rc) {
printf("\nError - return code from pthread_create is %d\n", rc);
return EXIT_FAILURE;
}
while (collision) {
collision = checkCollision(&player_position);
printGame(game);
msleep(TIME);
clearScreen();
updateGame(game, &player_position, direction, i, &length);
counter++;
if ((counter % fruit_interval) == 0) {
generateFruit(game);
}
}
total_time = TIME*counter/1000;
printf("\n*** GAME OVER! ***\n\n");
printf("\nTotal time: %.1fs\n\n", total_time);
puts("Press any key to quit ...");
free(direction);
pthread_exit(NULL);
}
I have absolutely no idea why that happens.
This happens because you've corrupted heap in some way. Common ways: freeing something twice, freeing unallocated (e.g. pointing to stack) pointer, writing before or after the end of the block, etc.
These heap corruption errors are often hard to debug using code inspection or a debugger, because the error is usually detected much later in the execution, possibly in completely unrelated code.
Fortunately, there are specialized tools which make finding these bugs much easier. If your platform supports Address Sanitizer, you are in luck: simply rebuild your program with gcc -fsanitize=address -g ... and it will point you right at the error. Valgrind is an earlier tool which can also help with the same class of bugs.
Here is output from running the game with -fsanitize=address:
$ ./a.out
Reallocing memory ...
Current size: 2
Memory reallocating sucessful!
=================================================================
==1713589==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 at pc 0x563c5223c583 bp 0x7ffdacfb9a10 sp 0x7ffdacfb9a08
READ of size 4 at 0x602000000010 thread T0
#0 0x563c5223c582 in updateGame /tmp/t.c:204
#1 0x563c5223cd5f in main /tmp/t.c:284
#2 0x7f8cb9f64e49 in __libc_start_main ../csu/libc-start.c:314
#3 0x563c5223b2c9 in _start (/tmp/a.out+0x22c9)
0x602000000010 is located 0 bytes inside of 8-byte region [0x602000000010,0x602000000018)
freed by thread T0 here:
#0 0x7f8cba1d1b48 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:164
#1 0x563c5223b3d8 in append /tmp/t.c:27
#2 0x563c5223c816 in updateGame /tmp/t.c:228
#3 0x563c5223cd5f in main /tmp/t.c:284
#4 0x7f8cb9f64e49 in __libc_start_main ../csu/libc-start.c:314
previously allocated by thread T0 here:
#0 0x7f8cba1d17cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
#1 0x563c5223cc3d in main /tmp/t.c:263
#2 0x7f8cb9f64e49 in __libc_start_main ../csu/libc-start.c:314
...
Heap use after free means: you've freed some memory (e.g. realloc frees the old memory block), but continue using it afterwards.
It's exceedingly likely that append should return v;, and the caller should be changed like so:
if (game[i][j] == '+') {
*length = *length+1;
- append(direction, *length, direction[it]);
+ direction = append(direction, *length, direction[it]);
}
game[i][j] = 'o';
However, even with above changes, other heap corruption bugs remain.
This is because (as dirck commented earlier), you have two instances of direction -- one in the main thread, and one in the readMoveone. The variable needs to be updated in both, and the access to this variable should be synchronized.

ncurses linked list not showing in screen

i made a linked list and filled it with 4 "#" characters, i wanted to print them in screen with the ncurses library because i'm trying to make a snake game but idk why it doesn't print, here's the code:
#include<ncurses.h>
#include<stdlib.h>
struct snake{
char piece;
struct snake *nextPiece;
};
struct snake *addNewPiece(const char piece){
struct snake *newPiece = NULL;
newPiece = malloc(sizeof(struct snake));
if(newPiece != NULL){
newPiece->nextPiece = NULL;
newPiece->piece = piece;
}else{
endwin();
printf("mem aloc failure, the program couldn't alloc the memory correctly");
}
return newPiece;
}
int main(){
int i = 0;
struct snake *first = NULL;
struct snake *added = NULL;
initscr();
noecho();
int yMaxStd,xMaxStd;
getmaxyx(stdscr,yMaxStd,xMaxStd);
WINDOW* win = newwin(20,50,(yMaxStd/2)-10,10);
box(win,0,0);
refresh();
wrefresh(win);
leaveok(win, true);
int yMaxWin,xMaxWin;
getmaxyx(win,yMaxWin,xMaxWin);
wmove(win,yMaxWin/2,yMaxWin/2);
//llenar lista con 4 #
while(i<=4){
if(first == NULL){
first = addNewPiece("#");
if(first != NULL){
added = first;
}
}else{
added->nextPiece = addNewPiece("#");
if(added->nextPiece != NULL){
added = added->nextPiece;
}
}
}
if(first == NULL){
endwin();
printf("Men alloc failure.");
}
printinscreen(first,win);
getch();
endwin();
return 0;
}
void printinscreen(struct snake *piece,WINDOW* win){
struct snake *t;
t = piece;
int y = 5,x = 5;
if(t == NULL){
endwin();
}else{
while(t != NULL){
mvwprintw(win,y,x,t);
t = t->nextPiece;
y--;
}
}
}
i know that im making good the linking process of the linked list becuase i tested it printing it with stdio.h in another file and it worked
Your programs have several bugs, I have fixed them. The most serious is that your code cannot be compiled in C++. If it's intended to build it under C++, you need to fix the compile errors at first.
newPiece = malloc(sizeof(struct snake));, need a convertion here: newPiece = (snake *)malloc(sizeof(struct snake));, generally it's not recomended to use malloc in c++, it's more naturally to use new
first = addNewPiece("#"); Pass string literal as char argument, need to pass a character here.
mvwprintw(win, y, x, t);, you have misunderstand the API, should be fixed as mvwprintw(win, y, x, "%c", t->piece);, you need to see the document before use a library API to see what types of arguments it expects
You forget to refresh the screen after printing the screen!
You haven't increment the index in for loop, it's an infinite loop.
Your code is somewhat c style, if you are trying to write with C++, it needs to be refactored, some suggestions:
use std::vector to store the snake body, then we don't need to manage the memory by hand. And avoid the error-prone linked list processing. Then most part of your code can be simplified.
Use a logging library and print logs to help to debug
The fixed code should work, I get a vertical snake on my console.
#include <ncurses.h>
#include <stdlib.h>
struct snake {
char piece;
struct snake *nextPiece;
};
struct snake *addNewPiece(const char piece) {
struct snake *newPiece = NULL;
newPiece = (snake *)malloc(sizeof(struct snake));
if (newPiece != NULL) {
newPiece->nextPiece = NULL;
newPiece->piece = piece;
} else {
endwin();
printf("mem aloc failure, the program couldn't alloc the memory correctly");
}
return newPiece;
}
void printinscreen(struct snake *piece, WINDOW *win);
int main(int argc, char *argv[]) {
int i = 0;
struct snake *first = NULL;
struct snake *added = NULL;
initscr();
noecho();
int yMaxStd, xMaxStd;
getmaxyx(stdscr, yMaxStd, xMaxStd);
WINDOW *win = newwin(20, 50, (yMaxStd / 2) - 10, 10);
box(win, 0, 0);
refresh();
wrefresh(win);
leaveok(win, true);
int yMaxWin, xMaxWin;
getmaxyx(win, yMaxWin, xMaxWin);
wmove(win, yMaxWin / 2, yMaxWin / 2);
// llenar lista con 4 #
while (i <= 4) {
if (first == NULL) {
first = addNewPiece('#');
if (first != NULL) {
added = first;
}
} else {
added->nextPiece = addNewPiece('#');
if (added->nextPiece != NULL) {
added = added->nextPiece;
}
}
++i;
}
if (first == NULL) {
endwin();
printf("Men alloc failure.");
}
printinscreen(first, win);
refresh();
wrefresh(win);
getch();
endwin();
return 0;
}
void printinscreen(struct snake *piece, WINDOW *win) {
struct snake *t;
t = piece;
int y = 5, x = 5;
if (t == NULL) {
endwin();
} else {
while (t != NULL) {
mvwprintw(win, y, x, "%c", t->piece);
t = t->nextPiece;
y--;
}
}
}
After seeing the PO's comments, we know that the question have been falsely tagged with c++, the c version code:
#include <ncurses.h>
#include <stdlib.h>
struct snake {
char piece;
struct snake *nextPiece;
};
struct snake *addNewPiece(const char piece) {
struct snake *newPiece = NULL;
newPiece = malloc(sizeof(struct snake));
if (newPiece != NULL) {
newPiece->nextPiece = NULL;
newPiece->piece = piece;
} else {
endwin();
printf("mem aloc failure, the program couldn't alloc the memory correctly");
}
return newPiece;
}
void printinscreen(struct snake *piece, WINDOW *win);
int main(int argc, char *argv[]) {
int i = 0;
struct snake *first = NULL;
struct snake *added = NULL;
initscr();
noecho();
int yMaxStd, xMaxStd;
getmaxyx(stdscr, yMaxStd, xMaxStd);
WINDOW *win = newwin(20, 50, (yMaxStd / 2) - 10, 10);
box(win, 0, 0);
refresh();
wrefresh(win);
leaveok(win, true);
int yMaxWin, xMaxWin;
getmaxyx(win, yMaxWin, xMaxWin);
wmove(win, yMaxWin / 2, yMaxWin / 2);
// llenar lista con 4 #
while (i <= 4) {
if (first == NULL) {
first = addNewPiece('#');
if (first != NULL) {
added = first;
}
} else {
added->nextPiece = addNewPiece('#');
if (added->nextPiece != NULL) {
added = added->nextPiece;
}
}
++i;
}
if (first == NULL) {
endwin();
printf("Men alloc failure.");
}
printinscreen(first, win);
refresh();
wrefresh(win);
getch();
endwin();
return 0;
}
void printinscreen(struct snake *piece, WINDOW *win) {
struct snake *t;
t = piece;
int y = 5, x = 5;
if (t == NULL) {
endwin();
} else {
while (t != NULL) {
mvwprintw(win, y, x, "%c", t->piece);
t = t->nextPiece;
y--;
}
}
}

Array of SDL Surfaces changes contents after a certain number of reallocations

Recently I've been trying to learn SDL, a graphics library amongst other things, for C. I haven't gotten very far but with the basics I've learnt from this tutorial (Yes, I know it's for C++ but it seems the majority of things are still the same), I've tried to create a "template" SDL program to start all my other future programs from. This is what I had:
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags);
void mainLoop(SDL_Window *window, SDL_Surface *surface);
void closeProg(SDL_Window *window);
SDL_Surface *loadImage(char *path);
int main(int argc, char *args[]) {
SDL_Window *window = NULL;
SDL_Surface *surface = NULL;
char windowName[13] = "SDL Tutorial";
int windowXPos = SDL_WINDOWPOS_UNDEFINED;
int windowYPos = SDL_WINDOWPOS_UNDEFINED;
int windowWidth = 600;
int windowHeight = 600;
int flags = SDL_WINDOW_SHOWN;
if (!initProg(&window, &surface, windowName, windowXPos, windowYPos, windowWidth, windowHeight, flags)) {
return 1;
}
mainLoop(window, surface);
closeProg(window);
return 0;
}
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("Failed to initialize SDL.\nError: %s\n", SDL_GetError());
return 0;
} else {
*window = SDL_CreateWindow(name, x, y, w, h, flags);
if (*window == NULL) {
printf("Failed to create a window.\nError:%s\n", SDL_GetError());
return 0;
} else {
*surface = SDL_GetWindowSurface(*window);
return 1;
}
}
}
void mainLoop(SDL_Window *window, SDL_Surface *surface) {
// Simple program to fade between white and black background
int g = 0;
int diff = -1;
int quit = 0;
SDL_Event e;
while (!quit) {
while(SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = 1;
}
}
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, g, g, g));
SDL_UpdateWindowSurface(window);
if (g == 0 || g == 255) {
diff *= -1;
}
g += diff;
SDL_Delay(10);
}
}
void closeProg(SDL_Window *window) {
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
}
The program is divided into three sections: one to initialize SDL, one where the main program occurs, and one to close SDL and free the surfaces.
I realized the problem with this is that if I had created another surface then I would have to add code to the closeProg() function to free the surface at the end. N.B: Keep in mind that these "surfaces" are just pointers to the actual thing, which as far as I'm aware you don't really interact with, you just deal with the pointer to it.
To work around this, I decided to create an array of these pointers-to-surfaces, and then make a function that creates a surface and adds it's pointer to the array. This would allow me at the end in the closeProg() function to go through each surface and free it, then free the array too. (Note that the window's surface is not added to this list as it gets freed automatically with the SDL_DestroyWindow() function)
Here is the declaration of this new array:
// The pointer to the array. Currently NULL until something is added
SDL_Surface **surfaces = NULL;
// Keeps track of the size of the array
size_t surfaceCount = 0;
This is the function that adds to the array:
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
size_t new_count = *surfaceCount + 1;
SDL_Surface **temp = realloc(surfaces, sizeof(*temp) * new_count);
if (temp == NULL) {
printf("Failed to reallocate to %d bytes of memory.", sizeof(*temp) * new_count);
return 0;
} else {
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
return 1;
}
}
And here it is in use. Note that loadImage() returns a surface from an image, and showSurfaces() prints out the contents of the surfaces array.
// Check contents before adding anything
showSurfaces(surfaces, *surfaceCount);
// Add an image
SDL_Surface *fire = loadImage("./fire.bmp");
addSurface(fire, surfaces, surfaceCount);
// Check contents again
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents again
SDL_Surface *ice = loadImage("./ice.bmp");
addSurface(ice, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents a final time
SDL_Surface *man = loadImage("./man.bmp");
addSurface(man, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
Up to the first two surfaces, everything went well. However, when I tried to add the third surface, the program hanged immediately before closing (indication that something is wrong in the closeProg() function?). I decided to print out the contents of the array and here is what I got.
No current surfaces.
Current surfaces:
Index 0: 00753C98
Current surfaces:
Index 0: 00753C98
Index 1: 00754780
Current surfaces:
Index 0: 02805150
Index 1: 008F00C0
Index 2: 201339FC
In the first two print-outs, everything seemed well but in the third one, you can notice that the previous addresses have changed. This happened repeatedly and no matter how many surfaces I added. After the first two reallocations, the array's content kept on changing.
I think that is why the program hangs when closing, because in the closeProg function, the program is told to free an unknown pointer that is not a surface, hence it crashes. Not to mention that I also set that pointer to NULL, and who knows what else that could cause.
Is this changing of content of the array normal? And if not, I would really appreciate if you could help me find what is causing this strange behavior. I repeat that I am a COMPLETE BEGINNER in this and so any help, even in a matter not relating to this question would be GREATLY appreciated. Thank you in advance :)
For reference, here are the images I used.
Here is the full code if you're interested:
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags);
void mainLoop(SDL_Window *window, SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount);
void closeProg(SDL_Window *window, SDL_Window **surfaces, size_t surfaceCount);
SDL_Surface *loadImage(char *path);
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount);
int main(int argc, char *args[]) {
SDL_Window *window = NULL;
SDL_Surface *surface = NULL;
// The pointer to the array. Currently NULL untill something is added
SDL_Surface **surfaces = (SDL_Surface *)calloc(1, sizeof(*surfaces));
// Keeps track of the size of the array
size_t surfaceCount = 0;
char windowName[13] = "SDL Tutorial";
int windowXPos = SDL_WINDOWPOS_UNDEFINED;
int windowYPos = SDL_WINDOWPOS_UNDEFINED;
int windowWidth = 600;
int windowHeight = 600;
int flags = SDL_WINDOW_SHOWN;
if (!initProg(&window, &surface, windowName, windowXPos, windowYPos, windowWidth, windowHeight, flags)) {
return 1;
}
mainLoop(window, surface, surfaces, &surfaceCount);
closeProg(window, surfaces, surfaceCount);
return 0;
}
int initProg(SDL_Window **window, SDL_Surface **surface, char *name, int x, int y, int w, int h, int flags) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("Failed to initialize SDL.\nError: %s\n", SDL_GetError());
return 0;
} else {
*window = SDL_CreateWindow(name, x, y, w, h, flags);
if (*window == NULL) {
printf("Failed to create a window.\nError:%s\n", SDL_GetError());
return 0;
} else {
*surface = SDL_GetWindowSurface(*window);
return 1;
}
}
}
void mainLoop(SDL_Window *window, SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
// Simple program to fade between white and black background
int g = 0;
int diff = -1;
// Check contents before adding anything
showSurfaces(surfaces, *surfaceCount);
// Add an image
SDL_Surface *fire = loadImage("./fire.bmp");
addSurface(fire, surfaces, surfaceCount);
// Check contents again
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents again
SDL_Surface *ice = loadImage("./ice.bmp");
addSurface(ice, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
// Add another image and check contents a final time
SDL_Surface *man = loadImage("./man.bmp");
addSurface(man, surfaces, surfaceCount);
showSurfaces(surfaces, *surfaceCount);
int quit = 0;
SDL_Event e;
while (!quit) {
while(SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = 1;
}
}
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, g, g, g));
SDL_UpdateWindowSurface(window);
if (g == 0 || g == 255) {
diff *= -1;
}
g += diff;
SDL_Delay(10);
}
}
void closeProg(SDL_Window *window, SDL_Window **surfaces, size_t surfaceCount) {
// Go through the array and free each surface.
for (int i = 0; i < surfaceCount; i++){
SDL_FreeSurface(*(surfaces + i));
*(surfaces + i) = NULL;
}
// Free the array itself.
free(surfaces);
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
}
SDL_Surface *loadImage(char *path) {
SDL_Surface *image = SDL_LoadBMP(path);
if (image == NULL) {
printf("Failed to load image.\nError: %s\n", SDL_GetError());
}
return image;
}
int addSurface(SDL_Surface *surface, SDL_Surface **surfaces, size_t *surfaceCount) {
size_t new_count = *surfaceCount + 1;
SDL_Surface **temp = realloc(surfaces, sizeof(*temp) * new_count);
if (temp == NULL) {
printf("Failed to reallocate to %d bytes of memory.", sizeof(*temp) * new_count);
return 0;
} else {
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
return 1;
}
}
void showSurfaces(SDL_Surface **surfaces, size_t surfaceCount) {
if (surfaceCount == 0) {
printf("\nNo current surfaces.\n");
} else {
printf("\nCurrent surfaces:\n");
for (int i = 0; i < surfaceCount; i++) {
printf("\tIndex %d: %p\n", i, *(surfaces + i));
}
putchar('\n');
}
}
If you can replicate this error, please comment down below so I know it's not something wrong with my machine or anything like that.
NOTE: I am using SDL 2.0
I realized the problem with this is that if I had created another
surface then I would have to add code to the closeProg() function to
free the surface at the end.
Not really. It's cleanest to do that, but the OS can be relied upon to release the program's memory, including any remaining dynamically allocated memory, when the program terminates. You could as easily just hold the allocated memory until then, instead of freeing it explicitly just prior to termination.
Anyway,
In the first two print-outs, everything seemed well but in the third one, you can notice that the previous addresses have changed. This
happened repeatedly and no matter how many surfaces I added. After the
first two reallocations, the array's content kept on changing.
I think that is why the program hangs when closing, because in the
closeProg function, the program is told to free an unknown pointer
that is not a surface, hence it crashes. Not to mention that I also
set that pointer to NULL, and who knows what else that could cause.
I think your analysis is plausible.
Is this changing of content of the array normal?
No. Reallocation with realloc() preserves the contents of the original space, copying it to the new space if it does not overlap, up to the lesser of the sizes of the two spaces.
I would really appreciate if you could help me find what is causing
this strange behavior.
Your addSurface() function is flawed. Although you have done well with the details of the reallocation itself, accounting both for the possibility that reallocation does not occur in place and the possibility that it fails, you do not successfully convey the new pointer to the caller.
In particular, consider this excerpt:
surfaces = temp;
*(surfaces + new_count - 1) = surface;
*surfaceCount = new_count;
The surface and surfaceCount variables are both function parameters. You seem to understand that in order to convey a new surface count value back to the caller via the latter, it must be a pointer to a variable accessible to the caller, so that the function can update that value via the pointer. That's what the last line of the excerpt does.
The situation is no different for the surfaces pointer. When in the first line of the excerpt you assign to surfaces, you are not making a change that will be visible to the caller. You're only modifying the value of a local variable inside the function. You must either add a layer of indirection for surface, or else convey the new pointer back to the caller via the function's return value. Personally, I'd go with the latter, because three-* programming is not widely accepted as good style.

gtk display modified image

I'm changing an image's pixbuf data in response to a timer event but the changes only appear if I, for example, cover the window with another window and then uncover it. How can I get the changes to appear as soon as I make them?
#include <gtk/gtk.h>
#include <stdlib.h>
#define ROWS 400
#define COLS 400 // must be divisible by 4
#define BYTES_PER_PIXEL 3
typedef struct {
GtkImage *image;
int stride;
} ImageData;
void free_pixels(guchar *pixels, gpointer data) {
free(pixels);
}
void setrgb(guchar *a, int row, int col, int stride,
guchar r, guchar g, guchar b) {
int p = row * stride + col * BYTES_PER_PIXEL;
a[p] = r; a[p+1] = g; a[p+2] = b;
}
int update_pic(gpointer data) {
static int row = 0;
if (row > 100) return FALSE;
ImageData *id = (ImageData*)data;
GdkPixbuf *pb = gtk_image_get_pixbuf(id->image);
guchar *g = gdk_pixbuf_get_pixels(pb);
for (int c = 0; c < 200; c++)
setrgb(g, row, c, id->stride, 255, 0, 0);
row++;
// this is not enough to get it to show the updated picture
gtk_widget_queue_draw(GTK_WIDGET(id->image));
// adding this does not fix it
while (g_main_context_pending(NULL))
g_main_context_iteration(NULL, FALSE);
return TRUE;
}
int main(int argc, char **argv) {
GtkWidget *window, *image;
GdkPixbuf *pb;
guchar *pixels = calloc(ROWS * COLS, BYTES_PER_PIXEL);
ImageData id;
gtk_init(&argc, &argv);
image = gtk_image_new();
id.image = GTK_IMAGE(image);
id.stride = COLS * BYTES_PER_PIXEL; // COLS is divisible by 4
pb = gdk_pixbuf_new_from_data(
pixels,
GDK_COLORSPACE_RGB, // colorspace
0, // has_alpha
8, // bits-per-sample
COLS, ROWS, // cols, rows
id.stride, // rowstride
free_pixels, // destroy_fn
NULL // destroy_fn_data
);
gtk_image_set_from_pixbuf(GTK_IMAGE(image), pb);
g_object_unref(pb); // should I do this?
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "image");
gtk_window_set_default_size(GTK_WINDOW(window), COLS, ROWS);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window), image);
g_timeout_add(250, // milliseconds
update_pic, // handler function
&id); // data
gtk_widget_show_all(window);
gtk_main();
return 0;
}
GtkImage is not a general drawing widget, for that you would need GtkDrawingArea. I think the simplest way to do what you want with the GtkImage is to update your GdkPixBuf then set the GtkImage with the updated image. I have increased the reference count to the PixBuf, but I am not 100% sure that it is needed, but it does not do any harm.
#include <gtk/gtk.h>
#include <stdlib.h>
#define ROWS 400
#define COLS 400 // must be divisible by 4
#define BYTES_PER_PIXEL 3
typedef struct {
GtkImage *image;
GdkPixbuf *pb;
int stride;
} ImageData;
void free_pixels(guchar *pixels, gpointer data) {
free(pixels);
}
void setrgb(guchar *a, int row, int col, int stride,
guchar r, guchar g, guchar b) {
int p = row * stride + col * BYTES_PER_PIXEL;
a[p] = r; a[p+1] = g; a[p+2] = b;
}
int update_pic(gpointer data) {
static int row = 0;
if (row > 100) return FALSE;
ImageData *id = (ImageData*)data;
guchar *g = gdk_pixbuf_get_pixels(id->pb);
for (int c = 0; c < 200; c++)
setrgb(g, row, c, id->stride, 255, 0, 0);
row++;
// Update the image, by setting it.
gtk_image_set_from_pixbuf(GTK_IMAGE(id->image), id->pb);
return TRUE;
}
int main(int argc, char **argv) {
GtkWidget *window;
guchar *pixels = calloc(ROWS * COLS, BYTES_PER_PIXEL);
ImageData id;
gtk_init(&argc, &argv);
id.stride = COLS * BYTES_PER_PIXEL; // COLS is divisible by 4
id.pb = gdk_pixbuf_new_from_data(
pixels,
GDK_COLORSPACE_RGB, // colorspace
0, // has_alpha
8, // bits-per-sample
COLS, ROWS, // cols, rows
id.stride, // rowstride
free_pixels, // destroy_fn
NULL // destroy_fn_data
);
id.image = GTK_IMAGE(gtk_image_new_from_pixbuf(id.pb));
g_object_ref(id.pb);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "image");
gtk_window_set_default_size(GTK_WINDOW(window), COLS, ROWS);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window), image);
g_timeout_add(250, // milliseconds
update_pic, // handler function
&id); // data
gtk_widget_show_all(window);
gtk_main();
g_object_unref(id.pb);
return 0;
}

Resources