Stop sharing requirement_unit_state_ereq().
[freeciv.git] / client / update_queue.c
blobb04985448740011a9cd42714622cde4823afec4a
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)
6 any later version.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 /* utility */
19 #include "log.h"
20 #include "support.h" /* bool */
22 /* common */
23 #include "city.h"
24 #include "connection.h"
25 #include "player.h"
27 /* client/include */
28 #include "canvas_g.h"
29 #include "citydlg_g.h"
30 #include "cityrep_g.h"
31 #include "dialogs_g.h"
32 #include "gui_main_g.h"
33 #include "menu_g.h"
34 #include "pages_g.h"
35 #include "plrdlg_g.h"
36 #include "ratesdlg_g.h"
37 #include "repodlgs_g.h"
39 /* client */
40 #include "client_main.h"
41 #include "connectdlg_common.h"
42 #include "options.h"
43 #include "zoom.h"
45 #include "update_queue.h"
48 /* Data type in 'update_queue'. */
49 struct update_queue_data {
50 void *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
61 #include "spechash.h"
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
78 #include "speclist.h"
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
88 #include "spechash.h"
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;
110 return uq_data;
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);
122 free(uq_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);
136 return wq_data;
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);
149 free(wq_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;
161 return uq_data;
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);
174 return;
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);
197 update_queue = NULL;
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()) {
244 update_queue_thaw();
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 ****************************************************************************/
259 static inline void
260 waiting_queue_execute_pending_requests(struct waiting_queue_hash *hash,
261 int request_id)
263 struct waiting_queue_list *list;
265 if (NULL == hash || !waiting_queue_hash_lookup(hash, request_id, &list)) {
266 return;
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,
281 request_id);
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,
290 request_id);
293 /****************************************************************************
294 Unqueue all updates.
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;
302 return;
305 if (update_queue_is_frozen()) {
306 /* Cannot update now, let's add it again. */
307 update_queue_has_idle_callback = FALSE;
308 return;
311 /* Replace the old list, don't do recursive stuff, and don't write in the
312 * hash table when we are reading it. */
313 hash = update_queue;
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
375 be NULL.
376 ****************************************************************************/
377 bool update_queue_has_callback_full(uq_callback_t callback,
378 const void **data,
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)) {
385 if (NULL != data) {
386 *data = uq_data->data;
388 if (NULL != free_data_func) {
389 *free_data_func = uq_data->free_data_func;
391 return TRUE;
394 return FALSE;
397 /****************************************************************************
398 Connects the callback to a network event.
399 ****************************************************************************/
400 static inline void
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)
405 if (NULL != hash) {
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,
414 free_data_func));
418 /****************************************************************************
419 Connects the callback to the start of the processing (in server side) of
420 the request.
421 ****************************************************************************/
422 void update_queue_connect_processing_started(int request_id,
423 uq_callback_t callback,
424 void *data)
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
432 the request.
433 ****************************************************************************/
434 void update_queue_connect_processing_started_full(int request_id,
435 uq_callback_t callback,
436 void *data,
437 uq_free_fn_t
438 free_data_func)
440 waiting_queue_add_pending_request(processing_started_waiting_queue,
441 request_id, callback, data,
442 free_data_func);
445 /****************************************************************************
446 Connects the callback to the end of the processing (in server side) of
447 the request.
448 ****************************************************************************/
449 void update_queue_connect_processing_finished(int request_id,
450 uq_callback_t callback,
451 void *data)
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
459 the request.
460 ****************************************************************************/
461 void update_queue_connect_processing_finished_full(int request_id,
462 uq_callback_t callback,
463 void *data,
464 uq_free_fn_t
465 free_data_func)
467 waiting_queue_add_pending_request(processing_finished_waiting_queue,
468 request_id, callback, data,
469 free_data_func);
473 /****************************************************************************
474 Set the client page.
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);
486 } else {
487 zoom_1_0();
493 /****************************************************************************
494 Set the client page.
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)
522 const void *data;
524 if (update_queue_has_callback_full(set_client_page_callback,
525 &data, NULL)) {
526 return FC_PTR_TO_INT(data);
527 } else {
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 /****************************************************************************
541 Update the menus.
542 ****************************************************************************/
543 static void menus_update_callback(void *data)
545 if (FC_PTR_TO_INT(data)) {
546 real_menus_init();
548 real_menus_update();
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 /****************************************************************************
578 Update cities gui.
579 ****************************************************************************/
580 static void cities_update_callback(void *data)
582 #ifdef FREECIV_DEBUG
583 #define NEED_UPDATE(city_update, action) \
584 if (city_update & need_update) { \
585 action; \
586 need_update &= ~city_update; \
588 #else /* FREECIV_DEBUG */
589 #define NEED_UPDATE(city_update, action) \
590 if (city_update & need_update) { \
591 action; \
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) {
599 continue;
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));
609 #ifdef FREECIV_DEBUG
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;
617 #undef NEED_UPDATE
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);