1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
15 #include <fc_config.h>
20 #include "support.h" /* bool */
24 #include "connection.h"
29 #include "citydlg_g.h"
30 #include "cityrep_g.h"
31 #include "dialogs_g.h"
32 #include "gui_main_g.h"
36 #include "ratesdlg_g.h"
37 #include "repodlgs_g.h"
40 #include "client_main.h"
41 #include "connectdlg_common.h"
45 #include "update_queue.h"
48 /* Data type in 'update_queue'. */
49 struct update_queue_data
{
51 uq_free_fn_t free_data_func
;
54 static void update_queue_data_destroy(struct update_queue_data
*pdata
);
56 /* 'struct update_queue_hash' and related functions. */
57 #define SPECHASH_TAG update_queue
58 #define SPECHASH_IKEY_TYPE uq_callback_t
59 #define SPECHASH_IDATA_TYPE struct update_queue_data *
60 #define SPECHASH_IDATA_FREE update_queue_data_destroy
62 #define update_queue_hash_iterate(hash, callback, uq_data) \
63 TYPED_HASH_ITERATE(uq_callback_t, const struct update_queue_data *, \
64 hash, callback, uq_data)
65 #define update_queue_hash_iterate_end HASH_ITERATE_END
67 /* Type of data listed in 'processing_started_waiting_queue' and
68 * 'processing_finished_waiting_queue'. Real type is
69 * 'struct waiting_queue_list'. */
70 struct waiting_queue_data
{
71 uq_callback_t callback
;
72 struct update_queue_data
*uq_data
;
75 /* 'struct waiting_queue_list' and related functions. */
76 #define SPECLIST_TAG waiting_queue
77 #define SPECLIST_TYPE struct waiting_queue_data
79 #define waiting_queue_list_iterate(list, data) \
80 TYPED_LIST_ITERATE(struct waiting_queue_data, list, data)
81 #define waiting_queue_list_iterate_end LIST_ITERATE_END
83 /* 'struct waiting_queue_hash' and related functions. */
84 #define SPECHASH_TAG waiting_queue
85 #define SPECHASH_INT_KEY_TYPE
86 #define SPECHASH_IDATA_TYPE struct waiting_queue_list *
87 #define SPECHASH_IDATA_FREE waiting_queue_list_destroy
90 static struct update_queue_hash
*update_queue
= NULL
;
91 static struct waiting_queue_hash
*processing_started_waiting_queue
= NULL
;
92 static struct waiting_queue_hash
*processing_finished_waiting_queue
= NULL
;
93 static int update_queue_frozen_level
= 0;
94 static bool update_queue_has_idle_callback
= FALSE
;
96 static void update_unqueue(void *data
);
97 static inline void update_queue_push(uq_callback_t callback
,
98 struct update_queue_data
*uq_data
);
100 /****************************************************************************
101 Create a new update queue data.
102 ****************************************************************************/
103 static inline struct update_queue_data
*
104 update_queue_data_new(void *data
, uq_free_fn_t free_data_func
)
106 struct update_queue_data
*uq_data
= fc_malloc(sizeof(*uq_data
));
108 uq_data
->data
= data
;
109 uq_data
->free_data_func
= free_data_func
;
113 /****************************************************************************
114 Free a update queue data.
115 ****************************************************************************/
116 static void update_queue_data_destroy(struct update_queue_data
*uq_data
)
118 fc_assert_ret(NULL
!= uq_data
);
119 if (NULL
!= uq_data
->free_data_func
) {
120 uq_data
->free_data_func(uq_data
->data
);
125 /****************************************************************************
126 Create a new waiting queue data.
127 ****************************************************************************/
128 static inline struct waiting_queue_data
*
129 waiting_queue_data_new(uq_callback_t callback
, void *data
,
130 uq_free_fn_t free_data_func
)
132 struct waiting_queue_data
*wq_data
= fc_malloc(sizeof(*wq_data
));
134 wq_data
->callback
= callback
;
135 wq_data
->uq_data
= update_queue_data_new(data
, free_data_func
);
139 /****************************************************************************
140 Free a waiting queue data.
141 ****************************************************************************/
142 static void waiting_queue_data_destroy(struct waiting_queue_data
*wq_data
)
144 fc_assert_ret(NULL
!= wq_data
);
145 if (NULL
!= wq_data
->uq_data
) {
146 /* May be NULL, see waiting_queue_data_extract(). */
147 update_queue_data_destroy(wq_data
->uq_data
);
152 /****************************************************************************
153 Extract the update_queue_data from the waiting queue data.
154 ****************************************************************************/
155 static inline struct update_queue_data
*
156 waiting_queue_data_extract(struct waiting_queue_data
*wq_data
)
158 struct update_queue_data
*uq_data
= wq_data
->uq_data
;
160 wq_data
->uq_data
= NULL
;
165 /****************************************************************************
166 Initialize the update queue.
167 ****************************************************************************/
168 void update_queue_init(void)
170 if (NULL
!= update_queue
) {
171 /* Already initialized. */
172 fc_assert(NULL
!= processing_started_waiting_queue
);
173 fc_assert(NULL
!= processing_finished_waiting_queue
);
176 fc_assert(NULL
== processing_started_waiting_queue
);
177 fc_assert(NULL
== processing_finished_waiting_queue
);
179 update_queue
= update_queue_hash_new();
180 processing_started_waiting_queue
= waiting_queue_hash_new();
181 processing_finished_waiting_queue
= waiting_queue_hash_new();
182 update_queue_frozen_level
= 0;
183 update_queue_has_idle_callback
= FALSE
;
186 /****************************************************************************
187 Free the update queue.
188 ****************************************************************************/
189 void update_queue_free(void)
191 fc_assert(NULL
!= update_queue
);
192 fc_assert(NULL
!= processing_started_waiting_queue
);
193 fc_assert(NULL
!= processing_finished_waiting_queue
);
195 if (NULL
!= update_queue
) {
196 update_queue_hash_destroy(update_queue
);
199 if (NULL
!= processing_started_waiting_queue
) {
200 waiting_queue_hash_destroy(processing_started_waiting_queue
);
201 processing_started_waiting_queue
= NULL
;
203 if (NULL
!= processing_finished_waiting_queue
) {
204 waiting_queue_hash_destroy(processing_finished_waiting_queue
);
205 processing_finished_waiting_queue
= NULL
;
208 update_queue_frozen_level
= 0;
209 update_queue_has_idle_callback
= FALSE
;
212 /****************************************************************************
213 Freezes the update queue.
214 ****************************************************************************/
215 void update_queue_freeze(void)
217 update_queue_frozen_level
++;
220 /****************************************************************************
221 Free the update queue.
222 ****************************************************************************/
223 void update_queue_thaw(void)
225 update_queue_frozen_level
--;
226 if (0 == update_queue_frozen_level
227 && !update_queue_has_idle_callback
228 && NULL
!= update_queue
229 && 0 < update_queue_hash_size(update_queue
)) {
230 update_queue_has_idle_callback
= TRUE
;
231 add_idle_callback(update_unqueue
, NULL
);
232 } else if (0 > update_queue_frozen_level
) {
233 log_error("update_queue_frozen_level < 0, reparing...");
234 update_queue_frozen_level
= 0;
238 /****************************************************************************
239 Free the update queue.
240 ****************************************************************************/
241 void update_queue_force_thaw(void)
243 while (update_queue_is_frozen()) {
248 /****************************************************************************
249 Free the update queue.
250 ****************************************************************************/
251 bool update_queue_is_frozen(void)
253 return (0 < update_queue_frozen_level
);
256 /****************************************************************************
257 Moves the instances waiting to the request_id to the callback queue.
258 ****************************************************************************/
260 waiting_queue_execute_pending_requests(struct waiting_queue_hash
*hash
,
263 struct waiting_queue_list
*list
;
265 if (NULL
== hash
|| !waiting_queue_hash_lookup(hash
, request_id
, &list
)) {
269 waiting_queue_list_iterate(list
, wq_data
) {
270 update_queue_push(wq_data
->callback
, waiting_queue_data_extract(wq_data
));
271 } waiting_queue_list_iterate_end
;
272 waiting_queue_hash_remove(hash
, request_id
);
275 /****************************************************************************
276 Moves the instances waiting to the request_id to the callback queue.
277 ****************************************************************************/
278 void update_queue_processing_started(int request_id
)
280 waiting_queue_execute_pending_requests(processing_started_waiting_queue
,
284 /****************************************************************************
285 Moves the instances waiting to the request_id to the callback queue.
286 ****************************************************************************/
287 void update_queue_processing_finished(int request_id
)
289 waiting_queue_execute_pending_requests(processing_finished_waiting_queue
,
293 /****************************************************************************
295 ****************************************************************************/
296 static void update_unqueue(void *data
)
298 struct update_queue_hash
*hash
;
300 if (NULL
== update_queue
) {
301 update_queue_has_idle_callback
= FALSE
;
305 if (update_queue_is_frozen()) {
306 /* Cannot update now, let's add it again. */
307 update_queue_has_idle_callback
= FALSE
;
311 /* Replace the old list, don't do recursive stuff, and don't write in the
312 * hash table when we are reading it. */
314 update_queue
= update_queue_hash_new();
315 update_queue_has_idle_callback
= FALSE
;
317 /* Invoke callbacks. */
318 update_queue_hash_iterate(hash
, callback
, uq_data
) {
319 callback(uq_data
->data
);
320 } update_queue_hash_iterate_end
;
321 update_queue_hash_destroy(hash
);
324 /****************************************************************************
325 Add a callback to the update queue. NB: you can only set a callback
326 once. Setting a callback twice will overwrite the previous.
327 ****************************************************************************/
328 static inline void update_queue_push(uq_callback_t callback
,
329 struct update_queue_data
*uq_data
)
331 update_queue_hash_replace(update_queue
, callback
, uq_data
);
333 if (!update_queue_has_idle_callback
334 && !update_queue_is_frozen()) {
335 update_queue_has_idle_callback
= TRUE
;
336 add_idle_callback(update_unqueue
, NULL
);
340 /****************************************************************************
341 Add a callback to the update queue. NB: you can only set a callback
342 once. Setting a callback twice will overwrite the previous.
343 ****************************************************************************/
344 void update_queue_add(uq_callback_t callback
, void *data
)
346 if (NULL
!= update_queue
) {
347 update_queue_push(callback
, update_queue_data_new(data
, NULL
));
351 /****************************************************************************
352 Add a callback to the update queue. NB: you can only set a callback
353 once. Setting a callback twice will overwrite the previous.
354 ****************************************************************************/
355 void update_queue_add_full(uq_callback_t callback
, void *data
,
356 uq_free_fn_t free_data_func
)
358 if (NULL
!= update_queue
) {
359 update_queue_push(callback
, update_queue_data_new(data
, free_data_func
));
363 /****************************************************************************
364 Returns whether this callback is listed in the update queue.
365 ****************************************************************************/
366 bool update_queue_has_callback(uq_callback_t callback
)
368 return (NULL
!= update_queue
369 && update_queue_hash_lookup(update_queue
, callback
, NULL
));
372 /****************************************************************************
373 Returns whether this callback is listed in the update queue and
374 get the its data and free function. 'data' and 'free_data_func' can
376 ****************************************************************************/
377 bool update_queue_has_callback_full(uq_callback_t callback
,
379 uq_free_fn_t
*free_data_func
)
381 if (NULL
!= update_queue
) {
382 struct update_queue_data
*uq_data
;
384 if (update_queue_hash_lookup(update_queue
, callback
, &uq_data
)) {
386 *data
= uq_data
->data
;
388 if (NULL
!= free_data_func
) {
389 *free_data_func
= uq_data
->free_data_func
;
397 /****************************************************************************
398 Connects the callback to a network event.
399 ****************************************************************************/
401 waiting_queue_add_pending_request(struct waiting_queue_hash
*hash
,
402 int request_id
, uq_callback_t callback
,
403 void *data
, uq_free_fn_t free_data_func
)
406 struct waiting_queue_list
*list
;
408 if (!waiting_queue_hash_lookup(hash
, request_id
, &list
)) {
409 /* The list doesn't exist yet for that request, create it. */
410 list
= waiting_queue_list_new_full(waiting_queue_data_destroy
);
411 waiting_queue_hash_insert(hash
, request_id
, list
);
413 waiting_queue_list_append(list
, waiting_queue_data_new(callback
, data
,
418 /****************************************************************************
419 Connects the callback to the start of the processing (in server side) of
421 ****************************************************************************/
422 void update_queue_connect_processing_started(int request_id
,
423 uq_callback_t callback
,
426 waiting_queue_add_pending_request(processing_started_waiting_queue
,
427 request_id
, callback
, data
, NULL
);
430 /****************************************************************************
431 Connects the callback to the start of the processing (in server side) of
433 ****************************************************************************/
434 void update_queue_connect_processing_started_full(int request_id
,
435 uq_callback_t callback
,
440 waiting_queue_add_pending_request(processing_started_waiting_queue
,
441 request_id
, callback
, data
,
445 /****************************************************************************
446 Connects the callback to the end of the processing (in server side) of
448 ****************************************************************************/
449 void update_queue_connect_processing_finished(int request_id
,
450 uq_callback_t callback
,
453 waiting_queue_add_pending_request(processing_finished_waiting_queue
,
454 request_id
, callback
, data
, NULL
);
457 /****************************************************************************
458 Connects the callback to the end of the processing (in server side) of
460 ****************************************************************************/
461 void update_queue_connect_processing_finished_full(int request_id
,
462 uq_callback_t callback
,
467 waiting_queue_add_pending_request(processing_finished_waiting_queue
,
468 request_id
, callback
, data
,
473 /****************************************************************************
475 ****************************************************************************/
476 static void set_client_page_callback(void *data
)
478 enum client_pages page
= FC_PTR_TO_INT(data
);
480 real_set_client_page(page
);
482 if (page
== PAGE_GAME
) {
483 if (has_zoom_support()) {
484 if (gui_options
.zoom_set
) {
485 zoom_set(gui_options
.zoom_default_level
);
493 /****************************************************************************
495 ****************************************************************************/
496 void set_client_page(enum client_pages page
)
498 log_debug("Requested page: %s.", client_pages_name(page
));
500 update_queue_add(set_client_page_callback
, FC_INT_TO_PTR(page
));
503 /****************************************************************************
504 Start server and then, set the client page.
505 ****************************************************************************/
506 void client_start_server_and_set_page(enum client_pages page
)
508 log_debug("Requested server start + page: %s.", client_pages_name(page
));
510 if (client_start_server()) {
511 update_queue_connect_processing_finished(client
.conn
.client
.last_request_id_used
,
512 set_client_page_callback
,
513 FC_INT_TO_PTR(page
));
517 /****************************************************************************
518 Returns the next client page.
519 ****************************************************************************/
520 enum client_pages
get_client_page(void)
524 if (update_queue_has_callback_full(set_client_page_callback
,
526 return FC_PTR_TO_INT(data
);
528 return get_current_client_page();
532 /****************************************************************************
533 Returns whether there's page switching already in progress.
534 ****************************************************************************/
535 bool update_queue_is_switching_page(void)
537 return update_queue_has_callback(set_client_page_callback
);
540 /****************************************************************************
542 ****************************************************************************/
543 static void menus_update_callback(void *data
)
545 if (FC_PTR_TO_INT(data
)) {
551 /****************************************************************************
552 Request the menus to be initialized and updated.
553 ****************************************************************************/
554 void menus_init(void)
556 update_queue_add(menus_update_callback
, FC_INT_TO_PTR(TRUE
));
559 /****************************************************************************
560 Request the menus to be updated.
561 ****************************************************************************/
562 void menus_update(void)
564 if (!update_queue_has_callback(menus_update_callback
)) {
565 update_queue_add(menus_update_callback
, FC_INT_TO_PTR(FALSE
));
569 /****************************************************************************
570 Update multipliers/policy dialog.
571 ****************************************************************************/
572 void multipliers_dialog_update(void)
574 update_queue_add(UQ_CALLBACK(real_multipliers_dialog_update
), NULL
);
577 /****************************************************************************
579 ****************************************************************************/
580 static void cities_update_callback(void *data
)
583 #define NEED_UPDATE(city_update, action) \
584 if (city_update & need_update) { \
586 need_update &= ~city_update; \
588 #else /* FREECIV_DEBUG */
589 #define NEED_UPDATE(city_update, action) \
590 if (city_update & need_update) { \
593 #endif /* FREECIV_DEBUG */
595 cities_iterate(pcity
) {
596 enum city_updates need_update
= pcity
->client
.need_updates
;
598 if (CU_NO_UPDATE
== need_update
) {
602 /* Clear all updates. */
603 pcity
->client
.need_updates
= CU_NO_UPDATE
;
605 NEED_UPDATE(CU_UPDATE_REPORT
, real_city_report_update_city(pcity
));
606 NEED_UPDATE(CU_UPDATE_DIALOG
, real_city_dialog_refresh(pcity
));
607 NEED_UPDATE(CU_POPUP_DIALOG
, real_city_dialog_popup(pcity
));
610 if (CU_NO_UPDATE
!= need_update
) {
611 log_error("Some city updates not handled "
612 "for city %s (id %d): %d left.",
613 city_name_get(pcity
), pcity
->id
, need_update
);
615 #endif /* FREECIV_DEBUG */
616 } cities_iterate_end
;
620 /****************************************************************************
621 Request the city dialog to be popped up for the city.
622 ****************************************************************************/
623 void popup_city_dialog(struct city
*pcity
)
625 pcity
->client
.need_updates
|= CU_POPUP_DIALOG
;
626 update_queue_add(cities_update_callback
, NULL
);
629 /****************************************************************************
630 Request the city dialog to be updated for the city.
631 ****************************************************************************/
632 void refresh_city_dialog(struct city
*pcity
)
634 pcity
->client
.need_updates
|= CU_UPDATE_DIALOG
;
635 update_queue_add(cities_update_callback
, NULL
);
638 /****************************************************************************
639 Request the city to be updated in the city report.
640 ****************************************************************************/
641 void city_report_dialog_update_city(struct city
*pcity
)
643 pcity
->client
.need_updates
|= CU_UPDATE_REPORT
;
644 update_queue_add(cities_update_callback
, NULL
);
648 /****************************************************************************
649 Update the connection list in the start page.
650 ****************************************************************************/
651 void conn_list_dialog_update(void)
653 update_queue_add(UQ_CALLBACK(real_conn_list_dialog_update
), NULL
);
657 /****************************************************************************
658 Update the nation report.
659 ****************************************************************************/
660 void players_dialog_update(void)
662 update_queue_add(UQ_CALLBACK(real_players_dialog_update
), NULL
);
665 /****************************************************************************
666 Update the city report.
667 ****************************************************************************/
668 void city_report_dialog_update(void)
670 update_queue_add(UQ_CALLBACK(real_city_report_dialog_update
), NULL
);
673 /****************************************************************************
674 Update the science report.
675 ****************************************************************************/
676 void science_report_dialog_update(void)
678 update_queue_add(UQ_CALLBACK(real_science_report_dialog_update
), NULL
);
681 /****************************************************************************
682 Update the economy report.
683 ****************************************************************************/
684 void economy_report_dialog_update(void)
686 update_queue_add(UQ_CALLBACK(real_economy_report_dialog_update
), NULL
);
689 /****************************************************************************
690 Update the units report.
691 ****************************************************************************/
692 void units_report_dialog_update(void)
694 update_queue_add(UQ_CALLBACK(real_units_report_dialog_update
), NULL
);
697 /****************************************************************************
698 Update the units report.
699 ****************************************************************************/
700 void unit_select_dialog_update(void)
702 update_queue_add(UQ_CALLBACK(unit_select_dialog_update_real
), NULL
);