/** * This function is a helper function for the wtk_frame_handler() function. * It is called when a pointer RELEASE event is sent to the container window. * This function completes the move operation if the RELEASE event was inside * the parent window's boundaries. * * \param frame Frame the received the event. * \param event Pointer event information. */ static void wtk_handle_frame_release(struct wtk_frame *frame, struct win_pointer_event const *event) { bool is_inside; switch (frame->state) { /* If we were moving when released, ungrab pointer and move if * release was inside parent window extents. */ case WTK_FRAME_MOVING: /* Ungrab pointer. */ wtk_stop_drag(&(event->last_pos)); win_grab_pointer(NULL); frame->state = WTK_FRAME_NORMAL; /* Check release position. */ is_inside = win_is_inside_window(win_get_parent(frame-> container), &(event->pos)); /* If inside, move frame according to drag distance. */ if (is_inside) { struct win_area area = *win_get_area(frame->container); area.pos.x += event->pos.x - wtk_drag_origin.x; area.pos.y += event->pos.y - wtk_drag_origin.y; win_set_area(frame->container, &area, WIN_ATTR_POSITION); } break; default: break; } }
/** * This function is a helper function for the wtk_frame_handler() function. * It is called when a pointer PRESS event is sent to the resize window. * This function prepares a drag operation. * * \param frame Frame the received the event. * \param event Pointer event information. */ static void wtk_handle_resize_press(struct wtk_frame *frame, struct win_pointer_event const *event) { switch (frame->state) { /* If we are in normal state, prepare for resize drag. */ case WTK_FRAME_NORMAL: wtk_prepare_drag(&(event->pos)); win_grab_pointer(frame->resize); frame->state = WTK_FRAME_AWAITING_RESIZE; break; default: break; } }
/** * This function is a helper function for the wtk_frame_handler() function. * It is called when a pointer RELEASE event is sent to the resize window. * This function completes the resize operation if the RELEASE event was inside * the parent window's boundaries. * * \param frame Frame the received the event. * \param event Pointer event information. */ static void wtk_handle_resize_release(struct wtk_frame *frame, struct win_pointer_event const *event) { bool is_inside; switch (frame->state) { /* If we were resizing when released, ungrab pointer and resize * if release was inside parent window extents. */ case WTK_FRAME_RESIZING: /* Ungrab pointer. */ wtk_stop_drag(&(event->last_pos)); win_grab_pointer(NULL); frame->state = WTK_FRAME_NORMAL; /* Check release position. */ is_inside = win_is_inside_window(win_get_parent(frame-> container), &(event->pos)); /* If inside, resize frame according to drag distance. */ if (is_inside) { struct win_area contents_area = *win_get_area(frame->contents); contents_area.size.x += event->pos.x - wtk_drag_origin.x; contents_area.size.y += event->pos.y - wtk_drag_origin.y; wtk_resize_frame(frame, &contents_area); } break; default: break; } }
/** * This function is the window event handler for check box widgets. * It handles all events sent to the windows composing the widget. * * \param win Window receiving the event. * \param type The event type. * \param data Custom data, depending on event type. * * \return True if the event was recognized and accepted. */ static bool wtk_check_box_handler(struct win_window *win, enum win_event_type type, void const *data) { struct win_command_event command; /* Custom data for windows of a widget points back to the widget itself. */ struct wtk_check_box *check_box = (struct wtk_check_box *)win_get_custom_data(win); switch (type) { case WIN_EVENT_DRAW: { /* There should not be other windows in this widget. */ Assert(win == check_box->container); /* For DRAW events, the data parameter points to the * clipping region. */ struct win_clip_region const *clip = (struct win_clip_region const *)data; /* Draw check box square. */ gfx_draw_rect(clip->origin.x + WTK_CHECKBOX_BOX_X, clip->origin.y + WTK_CHECKBOX_BOX_Y, WTK_CHECKBOX_BOX_SIZE, WTK_CHECKBOX_BOX_SIZE, WTK_CHECKBOX_BOX_COLOR); /* Draw check box square background. */ if (WTK_CHECKBOX_BACKGROUND_COLOR != GFX_COLOR_TRANSPARENT) { gfx_draw_filled_rect(clip->origin.x + WTK_CHECKBOX_BOX_X + 1, clip->origin.y + WTK_CHECKBOX_BOX_Y + 1, WTK_CHECKBOX_BOX_SIZE - 2, WTK_CHECKBOX_BOX_SIZE - 2, WTK_CHECKBOX_BACKGROUND_COLOR); } /* Draw check box select marker if selected. */ if (check_box->selected) { gfx_draw_filled_rect(clip->origin.x + WTK_CHECKBOX_BOX_X + 2, clip->origin.y + WTK_CHECKBOX_BOX_Y + 2, WTK_CHECKBOX_BOX_SIZE - 4, WTK_CHECKBOX_BOX_SIZE - 4, WTK_CHECKBOX_SELECT_COLOR); } /* Draw caption. */ gfx_draw_string(check_box->caption, clip->origin.x + WTK_CHECKBOX_CAPTION_X, clip->origin.y + WTK_CHECKBOX_CAPTION_Y, &sysfont, GFX_COLOR_TRANSPARENT, WTK_CHECKBOX_CAPTION_COLOR); /* Always accept DRAW events, as the return value is * ignored anyway for that event type. */ return true; } case WIN_EVENT_POINTER: { /* There should not be other windows in this widget. */ Assert(win == check_box->container); /* For POINTER events, the data parameter points to the * pointer event information. */ struct win_pointer_event const *event = (struct win_pointer_event const *)data; switch (event->type) { case WIN_POINTER_PRESS: /* When check box pressed, grab pointer and * wait for release inside widget borders. * Other widgets won't get pointer events * before it is released, and the pointer * ungrabbed by us. */ if (check_box->state == WTK_CHECKBOX_NORMAL) { win_grab_pointer(check_box->container); check_box->state = WTK_CHECKBOX_PRESSED; win_redraw(check_box->container); } break; case WIN_POINTER_RELEASE: /* When check box released, take action only if * released inside widget extents. */ if (check_box->state == WTK_CHECKBOX_PRESSED) { bool is_inside; /* Ungrab pointer. */ win_grab_pointer(NULL); check_box->state = WTK_CHECKBOX_NORMAL; win_redraw(check_box->container); /* Check release position. */ is_inside = win_is_inside_window (check_box->container, &(event->pos)); /* Toggle check box if inside. */ if (is_inside) { wtk_check_box_toggle(check_box); /* Send non-zero command. */ if (check_box->command) { command.sender = check_box->container; command.recipient = check_box->container; command.data = check_box->command; win_queue_command_event (&command); } } } break; default: break; } /* Accept all POINTER events since all acitivity inside * the widget extents is relevant to us. */ return true; } case WIN_EVENT_DESTROY: /* There should not be other windows in this widget. */ Assert(win == check_box->container); /* Memory allocated for windows will be automatically destroyed * by the window system. We must destroy other allocations. */ membag_free(check_box->caption); membag_free(check_box); /* Always accept DESTROY events, as the return value is ignored * anyway for that event type. */ return true; default: /* Reject unknown event types. */ return false; } }
/** * \brief Slider event handler. * * This is the window event handler for slider widgets. It handles the three * relevant event types sent to a slider's container window, i.e., drawing, * pointer and destroy events.\par * * For POINTER events, the slider value is only updated when the pointer moves, * and not upon press or release events. The handler will grab the pointer and * allow for it to move outside the actual widget window and still control the * slider knob. * * \param win Window receiving the event. * \param type The event type. * \param data Custom data, depending on event type. * * \return True if the event was recognized and accepted. */ static bool wtk_slider_handler(struct win_window *win, enum win_event_type type, void const *data) { struct win_pointer_event const *event; struct win_clip_region const *clip; struct win_command_event command; struct win_area const *area; struct wtk_slider *slider; struct win_point origin; gfx_color_t knob_color; gfx_coord_t position; gfx_coord_t length; uint8_t option; uint8_t value; bool send_command; slider = (struct wtk_slider *)win_get_custom_data(win); // There should not be other windows in this widget. assert(win == slider->container); switch (type) { case WIN_EVENT_DRAW: /* For DRAW events, the data parameter points to the * clipping region. */ clip = (struct win_clip_region const *)data; area = win_get_area(win); option = slider->option; if (slider->state == WTK_SLIDER_NORMAL) { knob_color = WTK_SLIDER_KNOB_COLOR_NORMAL; } else { knob_color = WTK_SLIDER_KNOB_COLOR_MOVING; } // Draw slider frame border. gfx_draw_rect(clip->origin.x, clip->origin.y, area->size.x, area->size.y, WTK_SLIDER_BORDER_COLOR); // Draw slider frame background within frame. gfx_draw_filled_rect(clip->origin.x + 1, clip->origin.y + 1, area->size.x - 2, area->size.y - 2, WTK_SLIDER_BACKGROUND_COLOR); // Draw the slider knob according to configured orientation. if (option & WTK_SLIDER_VERTICAL) { // Draw slider knob border. gfx_draw_rect(clip->origin.x, clip->origin.y + slider->position, area->size.x, WTK_SLIDER_KNOB_WIDTH, WTK_SLIDER_BORDER_COLOR); // Draw slider knob. gfx_draw_filled_rect(clip->origin.x + 1, clip->origin.y + slider->position + 1, area->size.x - 2, WTK_SLIDER_KNOB_WIDTH - 2, knob_color); } else { gfx_draw_rect(clip->origin.x + slider->position, clip->origin.y, WTK_SLIDER_KNOB_WIDTH, area->size.y, WTK_SLIDER_BORDER_COLOR); gfx_draw_filled_rect(clip->origin.x + slider->position + 1, clip->origin.y + 1, WTK_SLIDER_KNOB_WIDTH - 2, area->size.y - 2, knob_color); } /* Always accept DRAW events, as the return value is ignored * anyway for that event type. */ return true; case WIN_EVENT_POINTER: /* For POINTER events, the data parameter points to the pointer * event information. */ event = (struct win_pointer_event const *)data; area = win_get_area(win); option = slider->option; send_command = false; origin = slider->root_pos; switch (event->type) { case WIN_POINTER_PRESS: /* Grab the pointer and redraw a highlighted slider. * Slider value is not updated yet. */ if (slider->state == WTK_SLIDER_NORMAL) { slider->state = WTK_SLIDER_MOVING; win_grab_pointer(win); win_redraw(win); }; #if WTK_SLIDER_PARENT_MOVE_SUPPORT // Update root position. win_translate_win_to_root(win, &slider->root_pos); #endif break; case WIN_POINTER_MOVE: /* The slider is only updated when the pointer moves * and the window was previously pressed. The pointer * does not need to remain within the window to control * the knob as only the position along the slider's * direction of orientation is used. */ if (slider->state == WTK_SLIDER_MOVING) { /* Get the position of the pointer relative to * slider knob's origin, and the length of the * slider itself. */ if (option & WTK_SLIDER_VERTICAL) { position = event->pos.y - origin.y; length = area->size.y; } else { position = event->pos.x - origin.x; length = area->size.x; } // Subtract offsets due to slider knob size. position -= WTK_SLIDER_KNOB_WIDTH / 2; length -= WTK_SLIDER_KNOB_WIDTH; /* Bound the value if pointer is outside window. * Otherwise, compute the slider value from the * knob position. */ if (position < 0) { value = 0; } else if (position > length) { value = slider->maximum; } else { value = wtk_rescale_value(position, length, slider->maximum); } // Update slider only if this is a new value. if (slider->value != value) { slider->value = value; /* Compute knob position from value to * get correct positioning. */ slider->position = wtk_rescale_value(value, slider->maximum, length); if (option & WTK_SLIDER_CMD_MOVE) { send_command = true; } win_redraw(win); } } break; case WIN_POINTER_RELEASE: /* Release the pointer and redraw a normal slider. * The slider's value is not updated. Hence, a pointer * press directly followed by a release will leave the * slider value unchanged. */ if (slider->state == WTK_SLIDER_MOVING) { slider->state = WTK_SLIDER_NORMAL; win_grab_pointer(NULL); win_redraw(win); if (option & WTK_SLIDER_CMD_RELEASE) { send_command = true; } } break; default: break; } // Prepare and send command, if it was flagged. if (send_command) { command.sender = slider->container; command.recipient = slider->container; command.data = slider->command; win_queue_command_event(&command); } /* Accept all POINTER events since all acitivity inside the * widget window is relevant. */ return true; case WIN_EVENT_DESTROY: /* Memory allocated for windows will be automatically destroyed * by the window system. We must destroy other allocations. */ membag_free(slider); /* Always accept DESTROY events, as the return value is ignored * anyway for that event type. */ return true; default: // Reject unknown event types. return false; } }
/** * This function is the window event handler for radio button widgets. * It handles all events sent to the windows composing the widget. * * \param win Window receiving the event. * \param type The event type. * \param data Custom data, depending on event type. * * \return True if the event was recognized and accepted. */ static bool wtk_radio_button_handler(struct win_window *win, enum win_event_type type, void const *data) { struct win_command_event command; /* Custom data for windows of a widget points back to the widget itself. */ struct wtk_radio_button *radio_button = (struct wtk_radio_button *)win_get_custom_data(win); switch (type) { case WIN_EVENT_DRAW: { /* For DRAW events, the data parameter points to the * clipping region. */ struct win_clip_region const *clip = (struct win_clip_region const *)data; /* There should not be other windows in this widget. */ Assert(win == radio_button->container); /* Draw radio button circle. */ gfx_draw_circle(clip->origin.x + WTK_RADIOBUTTON_BUTTON_X, clip->origin.y + WTK_RADIOBUTTON_BUTTON_Y, WTK_RADIOBUTTON_RADIUS, WTK_RADIOBUTTON_BUTTON_COLOR, GFX_WHOLE); /* Draw radio button filled circle background. */ if (WTK_RADIOBUTTON_BACKGROUND_COLOR != GFX_COLOR_TRANSPARENT) { gfx_draw_filled_circle(clip->origin.x + WTK_RADIOBUTTON_BUTTON_X, clip->origin.y + WTK_RADIOBUTTON_BUTTON_Y, WTK_RADIOBUTTON_RADIUS - 1, WTK_RADIOBUTTON_BACKGROUND_COLOR, GFX_WHOLE); } /* Draw radio button select marker if selected. */ if (radio_button->group->selected == radio_button) { gfx_draw_filled_circle(clip->origin.x + WTK_RADIOBUTTON_BUTTON_X, clip->origin.y + WTK_RADIOBUTTON_BUTTON_Y, WTK_RADIOBUTTON_RADIUS - 2, WTK_RADIOBUTTON_SELECT_COLOR, GFX_WHOLE); } /* Draw caption. */ gfx_draw_string(radio_button->caption, clip->origin.x + WTK_RADIOBUTTON_CAPTION_X, clip->origin.y + WTK_RADIOBUTTON_CAPTION_Y, &sysfont, GFX_COLOR_TRANSPARENT, WTK_RADIOBUTTON_CAPTION_COLOR); /* Always accept DRAW events, as the return value is * ignored anyway for that event type. */ return true; } case WIN_EVENT_POINTER: { /* There should not be other windows in this widget. */ Assert(win == radio_button->container); /* For POINTER events, the data parameter points to the * pointer event information. */ struct win_pointer_event const *event = (struct win_pointer_event const *)data; switch (event->type) { case WIN_POINTER_PRESS: /* When radio button pressed, grab pointer and * wait for release inside widget borders. * Other widgets won't get pointer events * before it is released, and the pointer * ungrabbed by us. */ if (radio_button->state == WTK_RADIOBUTTON_NORMAL) { win_grab_pointer(radio_button-> container); radio_button->state = WTK_RADIOBUTTON_PRESSED; win_redraw(radio_button->container); } break; case WIN_POINTER_RELEASE: /* When button released, take action only if * released inside widget extents. */ if (radio_button->state == WTK_RADIOBUTTON_PRESSED) { bool is_inside; /* Ungrab pointer. */ win_grab_pointer(NULL); radio_button->state = WTK_RADIOBUTTON_NORMAL; win_redraw(radio_button->container); /* Check release position. */ is_inside = win_is_inside_window (radio_button-> container, &(event->pos)); /* Select this radio button if inside. */ if (is_inside) { wtk_radio_button_select (radio_button); /* Send non-zero command. */ if (radio_button->command) { command.sender = radio_button-> container; command.recipient = radio_button-> container; command.data = radio_button->command; win_queue_command_event (&command); } } } break; default: break; } /* Accept all POINTER events since all acitivity inside * the widget extents is relevant to us. */ return true; } case WIN_EVENT_DESTROY: /* There should not be other windows in this widget. */ Assert(win == radio_button->container); /* Memory allocated for windows will be automatically destroyed * by the window system. We must destroy other allocations. */ membag_free(radio_button->caption); /* Destroy radio group as well if we are the last one in the * group. If not, remove ourselves from the group. */ --(radio_button->group->num_references); if (!radio_button->group->num_references) { membag_free(radio_button->group); } else { if (radio_button->group->selected == radio_button) { radio_button->group->selected = NULL; } } membag_free(radio_button); /* Always accept DESTROY events, as the return value is ignored * anyway for that event type. */ return true; default: /* Reject unknown event types. */ return false; } }
/** * This function is the window event handler for icon button widgets. * It handles all events sent to the windows composing the widget. * * \param win Window receiving the event. * \param type The event type. * \param data Custom data, depending on event type. * * \return True if the event was recognized and accepted. */ static bool wtk_icon_button_handler(struct win_window *win, enum win_event_type type, void const *data) { struct win_command_event command; /* Custom data for windows of a widget points back to the widget itself. */ struct wtk_icon_button *icon_button = (struct wtk_icon_button *)win_get_custom_data(win); switch (type) { case WIN_EVENT_DRAW: { /* For DRAW events, the data parameter points to the * clipping region. */ struct win_clip_region const *clip = (struct win_clip_region const *)data; struct win_area const *area = win_get_area(win); /* There should not be other windows in this widget. */ Assert(win == icon_button->container); /* Draw icon button select marker if selected. */ if (icon_button->group->selected == icon_button) { gfx_draw_rect(clip->origin.x, clip->origin.y, area->size.x, area->size.y, WTK_ICONBUTTON_SELECT_COLOR); } /* Always accept DRAW events, as the return value is * ignored anyway for that event type. */ return true; } case WIN_EVENT_POINTER: { /* There should not be other windows in this widget. */ Assert(win == icon_button->container); /* For POINTER events, the data parameter points to the * pointer event information. */ struct win_pointer_event const *event = (struct win_pointer_event const *)data; switch (event->type) { case WIN_POINTER_PRESS: /* When icon button pressed, grab pointer and * wait for release inside widget borders. * Other widgets won't get pointer events * before it is released, and the pointer * ungrabbed by us. */ if (icon_button->state == WTK_ICONBUTTON_NORMAL) { win_grab_pointer(icon_button->container); icon_button->state = WTK_ICONBUTTON_PRESSED; win_redraw(icon_button->container); } break; case WIN_POINTER_RELEASE: /* When button released, take action only if * released inside widget extents. */ if (icon_button->state == WTK_ICONBUTTON_PRESSED) { bool is_inside; /* Ungrab pointer. */ win_grab_pointer(NULL); icon_button->state = WTK_ICONBUTTON_NORMAL; win_redraw(icon_button->container); /* Check release position. */ is_inside = win_is_inside_window (icon_button-> container, &(event->pos)); /* Select this icon button if inside. */ if (is_inside) { wtk_icon_button_select (icon_button); /* Send non-zero command. */ if (icon_button->command) { command.sender = icon_button-> container; command.recipient = icon_button-> container; command.data = icon_button->command; win_queue_command_event (&command); } } } break; default: break; } /* Accept all POINTER events since all activity inside * the widget extents is relevant to us. */ return true; } case WIN_EVENT_DESTROY: /* There should not be other windows in this widget. */ Assert(win == icon_button->container); /* Destroy icon group as well if we are the last one in the * group. If not, remove ourselves from the group. */ --(icon_button->group->num_references); if (!icon_button->group->num_references) { membag_free(icon_button->group); } else { if (icon_button->group->selected == icon_button) { icon_button->group->selected = NULL; } } membag_free(icon_button); /* Always accept DESTROY events, as the return value is ignored * anyway for that event type. */ return true; default: /* Reject unknown event types. */ return false; } }
/** * This function is the window event handler for button widgets. * It handles all events sent to the windows composing the widget. * * \param win Window receiving the event. * \param type The event type. * \param data Custom data, depending on event type. * * \return True if the event was recognized and accepted. */ static bool wtk_button_handler(struct win_window *win, enum win_event_type type, void const *data) { /* Custom data for windows of a widget points back to the widget itself. */ struct wtk_button *button = (struct wtk_button *)win_get_custom_data(win); switch (type) { case WIN_EVENT_DRAW: { /* For DRAW events, the data parameter points to the * clipping region. */ struct win_clip_region const *clip = (struct win_clip_region const *)data; struct win_area const *area = win_get_area(win); /* Preare background and caption colors depending on state of * button. If pressed/highlighted, the colors are inverted. */ gfx_color_t background_color; gfx_color_t caption_color; /* There should not be other windows in this widget. */ Assert(win == button->container); switch (button->state) { case WTK_BUTTON_NORMAL: background_color = WTK_BUTTON_BACKGROUND_COLOR; caption_color = WTK_BUTTON_CAPTION_COLOR; break; case WTK_BUTTON_PRESSED: background_color = WTK_BUTTON_CAPTION_COLOR; caption_color = WTK_BUTTON_BACKGROUND_COLOR; break; default: Assert(false); background_color = WTK_BUTTON_BACKGROUND_COLOR; caption_color = WTK_BUTTON_CAPTION_COLOR; } /* Draw background. */ gfx_draw_filled_rect(clip->origin.x, clip->origin.y, area->size.x, area->size.y, background_color); /* Draw border. */ gfx_draw_rect(clip->origin.x, clip->origin.y, area->size.x, area->size.y, WTK_BUTTON_BORDER_COLOR); /* Draw caption. */ gfx_draw_string_aligned(button->caption, clip->origin.x + (area->size.x / 2), clip->origin.y + (area->size.y / 2), &sysfont, GFX_COLOR_TRANSPARENT, caption_color, TEXT_POS_CENTER, TEXT_ALIGN_CENTER); /* Always accept DRAW events, as the return value is * ignored anyway for that event type. */ return true; } case WIN_EVENT_POINTER: { /* There should not be other windows in this widget. */ Assert(win == button->container); /* For POINTER events, the data parameter points to the * pointer event information. */ struct win_pointer_event const *event = (struct win_pointer_event const *)data; switch (event->type) { case WIN_POINTER_PRESS: /* When button pressed, grab pointer and wait * for release inside button borders. Other * widgets won't get pointer events before it * is released, and the pointer ungrabbed by * us. */ if (button->state == WTK_BUTTON_NORMAL) { win_grab_pointer(button->container); button->state = WTK_BUTTON_PRESSED; win_redraw(button->container); } break; case WIN_POINTER_RELEASE: /* When button released, take action only if * released inside widget extents. */ if (button->state == WTK_BUTTON_PRESSED) { /* Ungrab pointer. */ win_grab_pointer(NULL); button->state = WTK_BUTTON_NORMAL; win_redraw(button->container); /* Check release position. */ bool isInside = win_is_inside_window (button->container, &(event->pos)); /* Send command event if inside. */ if (isInside) { struct win_command_event command; command.sender = button-> container; command.recipient = button-> container; command.data = button-> command; win_queue_command_event (&command); } } break; default: break; } /* Accept all POINTER events since all acitivity inside * the widget extents is relevant to us. */ return true; } case WIN_EVENT_DESTROY: { /* There should not be other windows in this widget. */ Assert(win == button->container); /* Memory allocated for windows will be automatically * destroyed by the window system. We must destroy * other allocations. */ membag_free(button->caption); membag_free(button); /* Always accept DESTROY events, as the return value is * ignored anyway for that event type. */ return true; } default: /* Reject unknown event types. */ return false; } }
int main(int argc, char *argv[]) { byte keypress; client_init(); if( argc > 1 ) /* Command line arguments */ handle_cl_args(argc, argv); srand( time(NULL) ); calc_lookup_tables(DEGREES); win_init(); entity_init(); if( (ent_img_loaded = (char *)malloc(num_entity_types)) == NULL ) { perror("Malloc"); ERR_QUIT("Error allocating ent_img_loaded array", 1); } else { memset(ent_img_loaded, 0, num_entity_types); } /* Load client preferences from file */ read_config_file( &client ); /* Set view dimensions, which are calculated by tile width & heights */ client.view_w = client.x_tiles * TILE_W; client.view_h = client.y_tiles * TILE_H; /* What the client WISHES their dimensions were (this may not be what it actually is, ie in demo's etc */ client.desired_w = client.view_w; client.desired_h = client.view_h; weapon_type_init(); particle_init(); menu_init(); cl_network_init(); client.state = MENU; if( client.demo == DEMO_PLAY ) { cl_network_connect(NULL, 42); client.state = GAME_LOAD; } for( ; ; ) { switch( client.state ) { case MENU: win_ungrab_pointer(); menu_driver(); break; case GAME_LOAD: win_set_properties(VERSION, client.view_w, client.view_h+STATUS_H); wait_till_expose(5); /* Finished loading, we are ready to play */ cl_change_map(client.map, client.gamemode); break; case GAME_RESUME: /* Resume game from menu */ win_set_properties(VERSION, client.view_w, client.view_h+STATUS_H); wait_till_expose(5); input_clear(); draw_status_bar(); cl_netmsg_send_ready(); /* Tell server new details */ cl_net_finish(); /* Send the NETMSG_READY to the server */ client.state = GAME_JOIN; break; case GAME_JOIN: win_set_cursor( client.mousemode!=MOUSEMODE_ROTATE ); win_set_cursor( 1 ); if( client.mousemode ) { win_grab_pointer(); } text_buf_clear(); particle_clear(); case GAME_PLAY: cl_net_update(); if( client.state == GAME_PLAY ) { /* Maybe changed in net_update */ draw_crosshair( my_entity ); if( client.map_target_active ) draw_map_target( my_entity ); text_buf_update(); if( client.netstats ) cl_net_stats(); win_update(); /* Keyboard Input */ client.dir = my_entity.dir; keypress = get_input(); cl_netmsg_send_cl_update(keypress, client.dir); } cl_net_finish(); /* Send things that need sending */ cap_fps(client.fps); break; case QUIT: game_close(); image_close(); /* Free all the images */ win_close(); write_config_file(&client); if( ent_img_loaded != NULL ) free(ent_img_loaded); exit(EXIT_SUCCESS); break; } } return EXIT_SUCCESS; }