1 /*
2  * Copyright RIME Developers
3  * Distributed under the BSD License
4  *
5  * 2011-08-08  GONG Chen <chen.sst@gmail.com>  v0.9
6  * 2013-05-02  GONG Chen <chen.sst@gmail.com>  v1.0
7  */
8 #ifndef RIME_API_H_
9 #define RIME_API_H_
10 
11 #ifdef __cplusplus
12 extern "C" {
13 #endif
14 
15 #include <stddef.h>
16 #include <stdint.h>
17 
18 #if defined(_WIN32)
19 #if defined(RIME_EXPORTS)
20 /* DLL export */
21 #define RIME_API __declspec(dllexport)
22 #elif defined(RIME_IMPORTS)
23 /* DLL import */
24 #define RIME_API __declspec(dllimport)
25 #else
26 /* static library */
27 #define RIME_API
28 #endif
29 #else  /* _WIN32 */
30 #define RIME_API
31 #endif  /* _WIN32 */
32 
33 typedef uintptr_t RimeSessionId;
34 
35 typedef int Bool;
36 
37 #ifndef False
38 #define False 0
39 #endif
40 #ifndef True
41 #define True 1
42 #endif
43 
44 //! Define the max number of candidates
45 /*!
46  *  \deprecated There is no limit to the number of candidates in RimeMenu
47  */
48 #define RIME_MAX_NUM_CANDIDATES 10
49 
50 // Version control
51 #define RIME_STRUCT_INIT(Type, var) ((var).data_size = sizeof(Type) - sizeof((var).data_size))
52 #define RIME_STRUCT_HAS_MEMBER(var, member) ((int)(sizeof((var).data_size) + (var).data_size) > (char*)&member - (char*)&var)
53 #define RIME_STRUCT_CLEAR(var) memset((char*)&(var) + sizeof((var).data_size), 0, (var).data_size)
54 
55 //! Define a variable of Type
56 #define RIME_STRUCT(Type, var)  Type var = {0}; RIME_STRUCT_INIT(Type, var);
57 
58 //! For passing pointer to capnproto builder as opaque pointer through C API.
59 #define RIME_PROTO_BUILDER void
60 
61 //! Rime traits structure
62 /*!
63  *  Should be initialized by calling RIME_STRUCT_INIT(Type, var)
64  */
65 typedef struct rime_traits_t {
66   int data_size;
67   // v0.9
68   const char* shared_data_dir;
69   const char* user_data_dir;
70   const char* distribution_name;
71   const char* distribution_code_name;
72   const char* distribution_version;
73   // v1.0
74   /*!
75    * Pass a C-string constant in the format "rime.x"
76    * where 'x' is the name of your application.
77    * Add prefix "rime." to ensure old log files are automatically cleaned.
78    */
79   const char* app_name;
80 
81   //! A list of modules to load before initializing
82   const char** modules;
83   // v1.6
84   /*! Minimal level of logged messages.
85    *  Value is passed to Glog library using FLAGS_minloglevel variable.
86    *  0 = INFO (default), 1 = WARNING, 2 = ERROR, 3 = FATAL
87    */
88   int min_log_level;
89   /*! Directory of log files.
90    *  Value is passed to Glog library using FLAGS_log_dir variable.
91    */
92   const char* log_dir;
93   //! prebuilt data directory. defaults to ${shared_data_dir}/build
94   const char* prebuilt_data_dir;
95   //! staging directory. defaults to ${user_data_dir}/build
96   const char* staging_dir;
97 } RimeTraits;
98 
99 typedef struct {
100   int length;
101   int cursor_pos;
102   int sel_start;
103   int sel_end;
104   char* preedit;
105 } RimeComposition;
106 
107 typedef struct rime_candidate_t {
108   char* text;
109   char* comment;
110   void* reserved;
111 } RimeCandidate;
112 
113 typedef struct {
114   int page_size;
115   int page_no;
116   Bool is_last_page;
117   int highlighted_candidate_index;
118   int num_candidates;
119   RimeCandidate* candidates;
120   char* select_keys;
121 } RimeMenu;
122 
123 /*!
124  *  Should be initialized by calling RIME_STRUCT_INIT(Type, var);
125  */
126 typedef struct rime_commit_t {
127   int data_size;
128   // v0.9
129   char* text;
130 } RimeCommit;
131 
132 /*!
133  *  Should be initialized by calling RIME_STRUCT_INIT(Type, var);
134  */
135 typedef struct rime_context_t {
136   int data_size;
137   // v0.9
138   RimeComposition composition;
139   RimeMenu menu;
140   // v0.9.2
141   char* commit_text_preview;
142   char** select_labels;
143 } RimeContext;
144 
145 /*!
146  *  Should be initialized by calling RIME_STRUCT_INIT(Type, var);
147  */
148 typedef struct rime_status_t {
149   int data_size;
150   // v0.9
151   char* schema_id;
152   char* schema_name;
153   Bool is_disabled;
154   Bool is_composing;
155   Bool is_ascii_mode;
156   Bool is_full_shape;
157   Bool is_simplified;
158   Bool is_traditional;
159   Bool is_ascii_punct;
160 } RimeStatus;
161 
162 typedef struct rime_candidate_list_iterator_t {
163   void *ptr;
164   int index;
165   RimeCandidate candidate;
166 } RimeCandidateListIterator;
167 
168 typedef struct rime_config_t {
169   void* ptr;
170 } RimeConfig;
171 
172 typedef struct rime_config_iterator_t {
173   void* list;
174   void* map;
175   int index;
176   const char* key;
177   const char* path;
178 } RimeConfigIterator;
179 
180 typedef struct rime_schema_list_item_t {
181   char* schema_id;
182   char* name;
183   void* reserved;
184 } RimeSchemaListItem;
185 
186 typedef struct rime_schema_list_t {
187   size_t size;
188   RimeSchemaListItem* list;
189 } RimeSchemaList;
190 
191 typedef void (*RimeNotificationHandler)(void* context_object,
192                                         RimeSessionId session_id,
193                                         const char* message_type,
194                                         const char* message_value);
195 
196 // Setup
197 
198 /*!
199  *  Call this function before accessing any other API.
200  */
201 RIME_API void RimeSetup(RimeTraits *traits);
202 
203 /*!
204  *  Pass a C-string constant in the format "rime.x"
205  *  where 'x' is the name of your application.
206  *  Add prefix "rime." to ensure old log files are automatically cleaned.
207  *  \deprecated Use RimeSetup() instead.
208  */
209 RIME_API void RimeSetupLogging(const char* app_name);
210 
211 //! Receive notifications
212 /*!
213  * - on loading schema:
214  *   + message_type="schema", message_value="luna_pinyin/Luna Pinyin"
215  * - on changing mode:
216  *   + message_type="option", message_value="ascii_mode"
217  *   + message_type="option", message_value="!ascii_mode"
218  * - on deployment:
219  *   + session_id = 0, message_type="deploy", message_value="start"
220  *   + session_id = 0, message_type="deploy", message_value="success"
221  *   + session_id = 0, message_type="deploy", message_value="failure"
222  *
223  *   handler will be called with context_object as the first parameter
224  *   every time an event occurs in librime, until RimeFinalize() is called.
225  *   when handler is NULL, notification is disabled.
226  */
227 RIME_API void RimeSetNotificationHandler(RimeNotificationHandler handler,
228                                          void* context_object);
229 
230 // Entry and exit
231 
232 RIME_API void RimeInitialize(RimeTraits *traits);
233 RIME_API void RimeFinalize();
234 
235 RIME_API Bool RimeStartMaintenance(Bool full_check);
236 
237 //! \deprecated Use RimeStartMaintenance(full_check = False) instead.
238 RIME_API Bool RimeStartMaintenanceOnWorkspaceChange();
239 RIME_API Bool RimeIsMaintenancing();
240 RIME_API void RimeJoinMaintenanceThread();
241 
242 // Deployment
243 
244 RIME_API void RimeDeployerInitialize(RimeTraits *traits);
245 RIME_API Bool RimePrebuildAllSchemas();
246 RIME_API Bool RimeDeployWorkspace();
247 RIME_API Bool RimeDeploySchema(const char *schema_file);
248 RIME_API Bool RimeDeployConfigFile(const char *file_name, const char *version_key);
249 
250 RIME_API Bool RimeSyncUserData();
251 
252 // Session management
253 
254 RIME_API RimeSessionId RimeCreateSession();
255 RIME_API Bool RimeFindSession(RimeSessionId session_id);
256 RIME_API Bool RimeDestroySession(RimeSessionId session_id);
257 RIME_API void RimeCleanupStaleSessions();
258 RIME_API void RimeCleanupAllSessions();
259 
260 // Input
261 
262 RIME_API Bool RimeProcessKey(RimeSessionId session_id, int keycode, int mask);
263 /*!
264  * return True if there is unread commit text
265  */
266 RIME_API Bool RimeCommitComposition(RimeSessionId session_id);
267 RIME_API void RimeClearComposition(RimeSessionId session_id);
268 
269 // Output
270 
271 RIME_API Bool RimeGetCommit(RimeSessionId session_id, RimeCommit* commit);
272 RIME_API Bool RimeFreeCommit(RimeCommit* commit);
273 RIME_API Bool RimeGetContext(RimeSessionId session_id, RimeContext* context);
274 RIME_API Bool RimeFreeContext(RimeContext* context);
275 RIME_API Bool RimeGetStatus(RimeSessionId session_id, RimeStatus* status);
276 RIME_API Bool RimeFreeStatus(RimeStatus* status);
277 
278 // Accessing candidate list
279 RIME_API Bool RimeCandidateListBegin(RimeSessionId session_id, RimeCandidateListIterator* iterator);
280 RIME_API Bool RimeCandidateListNext(RimeCandidateListIterator* iterator);
281 RIME_API void RimeCandidateListEnd(RimeCandidateListIterator* iterator);
282 RIME_API Bool RimeCandidateListFromIndex(RimeSessionId session_id,
283                                          RimeCandidateListIterator* iterator,
284                                          int index);
285 
286 // Runtime options
287 
288 RIME_API void RimeSetOption(RimeSessionId session_id, const char* option, Bool value);
289 RIME_API Bool RimeGetOption(RimeSessionId session_id, const char* option);
290 
291 RIME_API void RimeSetProperty(RimeSessionId session_id, const char* prop, const char* value);
292 RIME_API Bool RimeGetProperty(RimeSessionId session_id, const char* prop, char* value, size_t buffer_size);
293 
294 RIME_API Bool RimeGetSchemaList(RimeSchemaList* schema_list);
295 RIME_API void RimeFreeSchemaList(RimeSchemaList* schema_list);
296 RIME_API Bool RimeGetCurrentSchema(RimeSessionId session_id, char* schema_id, size_t buffer_size);
297 RIME_API Bool RimeSelectSchema(RimeSessionId session_id, const char* schema_id);
298 
299 // Configuration
300 
301 // <schema_id>.schema.yaml
302 RIME_API Bool RimeSchemaOpen(const char* schema_id, RimeConfig* config);
303 // <config_id>.yaml
304 RIME_API Bool RimeConfigOpen(const char* config_id, RimeConfig* config);
305 // access config files in user data directory, eg. user.yaml and installation.yaml
306 RIME_API Bool RimeUserConfigOpen(const char* config_id, RimeConfig* config);
307 RIME_API Bool RimeConfigClose(RimeConfig* config);
308 RIME_API Bool RimeConfigInit(RimeConfig* config);
309 RIME_API Bool RimeConfigLoadString(RimeConfig* config, const char* yaml);
310 // Access config values
311 RIME_API Bool RimeConfigGetBool(RimeConfig *config, const char *key, Bool *value);
312 RIME_API Bool RimeConfigGetInt(RimeConfig *config, const char *key, int *value);
313 RIME_API Bool RimeConfigGetDouble(RimeConfig *config, const char *key, double *value);
314 RIME_API Bool RimeConfigGetString(RimeConfig *config, const char *key,
315                                   char *value, size_t buffer_size);
316 RIME_API const char* RimeConfigGetCString(RimeConfig *config, const char *key);
317 RIME_API Bool RimeConfigSetBool(RimeConfig *config, const char *key, Bool value);
318 RIME_API Bool RimeConfigSetInt(RimeConfig *config, const char *key, int value);
319 RIME_API Bool RimeConfigSetDouble(RimeConfig *config, const char *key, double value);
320 RIME_API Bool RimeConfigSetString(RimeConfig *config, const char *key, const char *value);
321 // Manipulate complex structures
322 RIME_API Bool RimeConfigGetItem(RimeConfig* config, const char* key, RimeConfig* value);
323 RIME_API Bool RimeConfigSetItem(RimeConfig* config, const char* key, RimeConfig* value);
324 RIME_API Bool RimeConfigClear(RimeConfig* config, const char* key);
325 RIME_API Bool RimeConfigCreateList(RimeConfig* config, const char* key);
326 RIME_API Bool RimeConfigCreateMap(RimeConfig* config, const char* key);
327 RIME_API size_t RimeConfigListSize(RimeConfig* config, const char* key);
328 RIME_API Bool RimeConfigBeginList(RimeConfigIterator* iterator, RimeConfig* config, const char* key);
329 RIME_API Bool RimeConfigBeginMap(RimeConfigIterator* iterator, RimeConfig* config, const char* key);
330 RIME_API Bool RimeConfigNext(RimeConfigIterator* iterator);
331 RIME_API void RimeConfigEnd(RimeConfigIterator* iterator);
332 // Utilities
333 RIME_API Bool RimeConfigUpdateSignature(RimeConfig* config, const char* signer);
334 
335 // Testing
336 
337 RIME_API Bool RimeSimulateKeySequence(RimeSessionId session_id, const char *key_sequence);
338 
339 // Module
340 
341 /*!
342  *  Extend the structure to publish custom data/functions in your specific module
343  */
344 typedef struct rime_custom_api_t {
345   int data_size;
346 } RimeCustomApi;
347 
348 typedef struct rime_module_t {
349   int data_size;
350 
351   const char* module_name;
352   void (*initialize)();
353   void (*finalize)();
354   RimeCustomApi* (*get_api)();
355 } RimeModule;
356 
357 RIME_API Bool RimeRegisterModule(RimeModule* module);
358 RIME_API RimeModule* RimeFindModule(const char* module_name);
359 
360 //! Run a registered task
361 RIME_API Bool RimeRunTask(const char* task_name);
362 
363 RIME_API const char* RimeGetSharedDataDir();
364 RIME_API const char* RimeGetUserDataDir();
365 RIME_API const char* RimeGetSyncDir();
366 RIME_API const char* RimeGetUserId();
367 
368 /*! The API structure
369  *  RimeApi is for rime v1.0+
370  */
371 typedef struct rime_api_t {
372   int data_size;
373 
374   /*! setup
375    *  Call this function before accessing any other API functions.
376    */
377   void (*setup)(RimeTraits* traits);
378 
379   /*! Set up the notification callbacks
380    *  Receive notifications
381    *  - on loading schema:
382    *    + message_type="schema", message_value="luna_pinyin/Luna Pinyin"
383    *  - on changing mode:
384    *    + message_type="option", message_value="ascii_mode"
385    *    + message_type="option", message_value="!ascii_mode"
386    *  - on deployment:
387    *    + session_id = 0, message_type="deploy", message_value="start"
388    *    + session_id = 0, message_type="deploy", message_value="success"
389    *    + session_id = 0, message_type="deploy", message_value="failure"
390    *
391    *  handler will be called with context_object as the first parameter
392    *  every time an event occurs in librime, until RimeFinalize() is called.
393    *  when handler is NULL, notification is disabled.
394    */
395   void (*set_notification_handler)(RimeNotificationHandler handler,
396                                    void* context_object);
397 
398   // entry and exit
399 
400   void (*initialize)(RimeTraits *traits);
401   void (*finalize)();
402 
403   Bool (*start_maintenance)(Bool full_check);
404   Bool (*is_maintenance_mode)();
405   void (*join_maintenance_thread)();
406 
407   // deployment
408 
409   void (*deployer_initialize)(RimeTraits *traits);
410   Bool (*prebuild)();
411   Bool (*deploy)();
412   Bool (*deploy_schema)(const char *schema_file);
413   Bool (*deploy_config_file)(const char *file_name, const char *version_key);
414 
415   Bool (*sync_user_data)();
416 
417   // session management
418 
419   RimeSessionId (*create_session)();
420   Bool (*find_session)(RimeSessionId session_id);
421   Bool (*destroy_session)(RimeSessionId session_id);
422   void (*cleanup_stale_sessions)();
423   void (*cleanup_all_sessions)();
424 
425   // input
426 
427   Bool (*process_key)(RimeSessionId session_id, int keycode, int mask);
428   // return True if there is unread commit text
429   Bool (*commit_composition)(RimeSessionId session_id);
430   void (*clear_composition)(RimeSessionId session_id);
431 
432   // output
433 
434   Bool (*get_commit)(RimeSessionId session_id, RimeCommit* commit);
435   Bool (*free_commit)(RimeCommit* commit);
436   Bool (*get_context)(RimeSessionId session_id, RimeContext* context);
437   Bool (*free_context)(RimeContext* ctx);
438   Bool (*get_status)(RimeSessionId session_id, RimeStatus* status);
439   Bool (*free_status)(RimeStatus* status);
440 
441   // runtime options
442 
443   void (*set_option)(RimeSessionId session_id, const char* option, Bool value);
444   Bool (*get_option)(RimeSessionId session_id, const char* option);
445 
446   void (*set_property)(RimeSessionId session_id, const char* prop, const char* value);
447   Bool (*get_property)(RimeSessionId session_id, const char* prop, char* value, size_t buffer_size);
448 
449   Bool (*get_schema_list)(RimeSchemaList* schema_list);
450   void (*free_schema_list)(RimeSchemaList* schema_list);
451 
452   Bool (*get_current_schema)(RimeSessionId session_id, char* schema_id, size_t buffer_size);
453   Bool (*select_schema)(RimeSessionId session_id, const char* schema_id);
454 
455   // configuration
456 
457   Bool (*schema_open)(const char *schema_id, RimeConfig* config);
458   Bool (*config_open)(const char *config_id, RimeConfig* config);
459   Bool (*config_close)(RimeConfig *config);
460   Bool (*config_get_bool)(RimeConfig *config, const char *key, Bool *value);
461   Bool (*config_get_int)(RimeConfig *config, const char *key, int *value);
462   Bool (*config_get_double)(RimeConfig *config, const char *key, double *value);
463   Bool (*config_get_string)(RimeConfig *config, const char *key,
464                             char *value, size_t buffer_size);
465   const char* (*config_get_cstring)(RimeConfig *config, const char *key);
466   Bool (*config_update_signature)(RimeConfig* config, const char* signer);
467   Bool (*config_begin_map)(RimeConfigIterator* iterator, RimeConfig* config, const char* key);
468   Bool (*config_next)(RimeConfigIterator* iterator);
469   void (*config_end)(RimeConfigIterator* iterator);
470 
471   // testing
472 
473   Bool (*simulate_key_sequence)(RimeSessionId session_id, const char *key_sequence);
474 
475   // module
476 
477   Bool (*register_module)(RimeModule* module);
478   RimeModule* (*find_module)(const char* module_name);
479 
480   Bool (*run_task)(const char* task_name);
481   const char* (*get_shared_data_dir)();
482   const char* (*get_user_data_dir)();
483   const char* (*get_sync_dir)();
484   const char* (*get_user_id)();
485   void (*get_user_data_sync_dir)(char* dir, size_t buffer_size);
486 
487   //! initialize an empty config object
488   /*!
489    * should call config_close() to free the object
490    */
491   Bool (*config_init)(RimeConfig* config);
492   //! deserialize config from a yaml string
493   Bool (*config_load_string)(RimeConfig* config, const char* yaml);
494 
495   // configuration: setters
496   Bool (*config_set_bool)(RimeConfig *config, const char *key, Bool value);
497   Bool (*config_set_int)(RimeConfig *config, const char *key, int value);
498   Bool (*config_set_double)(RimeConfig *config, const char *key, double value);
499   Bool (*config_set_string)(RimeConfig *config, const char *key, const char *value);
500 
501   // configuration: manipulating complex structures
502   Bool (*config_get_item)(RimeConfig* config, const char* key, RimeConfig* value);
503   Bool (*config_set_item)(RimeConfig* config, const char* key, RimeConfig* value);
504   Bool (*config_clear)(RimeConfig* config, const char* key);
505   Bool (*config_create_list)(RimeConfig* config, const char* key);
506   Bool (*config_create_map)(RimeConfig* config, const char* key);
507   size_t (*config_list_size)(RimeConfig* config, const char* key);
508   Bool (*config_begin_list)(RimeConfigIterator* iterator, RimeConfig* config, const char* key);
509 
510   //! get raw input
511   /*!
512    *  NULL is returned if session does not exist.
513    *  the returned pointer to input string will become invalid upon editing.
514    */
515   const char* (*get_input)(RimeSessionId session_id);
516 
517   //! caret posistion in terms of raw input
518   size_t (*get_caret_pos)(RimeSessionId session_id);
519 
520   //! select a candidate at the given index in candidate list.
521   Bool (*select_candidate)(RimeSessionId session_id, size_t index);
522 
523   //! get the version of librime
524   const char* (*get_version)();
525 
526   //! set caret posistion in terms of raw input
527   void (*set_caret_pos)(RimeSessionId session_id, size_t caret_pos);
528 
529   //! select a candidate from current page.
530   Bool (*select_candidate_on_current_page)(RimeSessionId session_id, size_t index);
531 
532   //! access candidate list.
533   Bool (*candidate_list_begin)(RimeSessionId session_id, RimeCandidateListIterator* iterator);
534   Bool (*candidate_list_next)(RimeCandidateListIterator* iterator);
535   void (*candidate_list_end)(RimeCandidateListIterator* iterator);
536 
537   //! access config files in user data directory, eg. user.yaml and installation.yaml
538   Bool (*user_config_open)(const char *config_id, RimeConfig* config);
539 
540   Bool (*candidate_list_from_index)(RimeSessionId session_id,
541                                     RimeCandidateListIterator* iterator,
542                                     int index);
543 
544   //! prebuilt data directory.
545   const char* (*get_prebuilt_data_dir)();
546   //! staging directory, stores data files deployed to a Rime client.
547   const char* (*get_staging_dir)();
548 
549   //! capnproto API.
550   void (*commit_proto)(RimeSessionId session_id, RIME_PROTO_BUILDER* commit_builder);
551   void (*context_proto)(RimeSessionId session_id, RIME_PROTO_BUILDER* context_builder);
552   void (*status_proto)(RimeSessionId session_id, RIME_PROTO_BUILDER* status_builder);
553 } RimeApi;
554 
555 //! API entry
556 /*!
557  *  Acquire the version controlled RimeApi structure.
558  */
559 RIME_API RimeApi* rime_get_api();
560 
561 //! Clients should test if an api function is available in the current version before calling it.
562 #define RIME_API_AVAILABLE(api, func) (RIME_STRUCT_HAS_MEMBER(*(api), (api)->func) && (api)->func)
563 
564 // Initializer for MSVC and GCC.
565 // 2010 Joe Lowe. Released into the public domain.
566 #if defined(__GNUC__)
567 #define RIME_MODULE_INITIALIZER(f) \
568   static void f(void) __attribute__((constructor)); \
569   static void f(void)
570 #elif defined(_MSC_VER)
571 #pragma section(".CRT$XCU",read)
572 #define RIME_MODULE_INITIALIZER(f) \
573   static void __cdecl f(void); \
574   __declspec(allocate(".CRT$XCU")) void (__cdecl*f##_)(void) = f; \
575   static void __cdecl f(void)
576 #endif
577 
578 /*!
579  *  Automatically register a rime module when the library is loaded.
580  *  Clients should define functions called rime_<module_name>_initialize(),
581  *  and rime_<module_name>_finalize().
582  *  \sa core_module.cc for an example.
583  */
584 #define RIME_REGISTER_MODULE(name) \
585 void rime_require_module_##name() {} \
586 RIME_MODULE_INITIALIZER(rime_register_module_##name) { \
587   static RimeModule module = {0}; \
588   if (!module.data_size) { \
589     RIME_STRUCT_INIT(RimeModule, module); \
590     module.module_name = #name; \
591     module.initialize = rime_##name##_initialize; \
592     module.finalize = rime_##name##_finalize; \
593   } \
594   rime_get_api()->register_module(&module); \
595 }
596 
597 /*!
598  *  Customize the module by assigning additional functions, eg. module->get_api.
599  */
600 #define RIME_REGISTER_CUSTOM_MODULE(name) \
601 void rime_require_module_##name() {} \
602 static void rime_customize_module_##name(RimeModule* module); \
603 RIME_MODULE_INITIALIZER(rime_register_module_##name) { \
604   static RimeModule module = {0}; \
605   if (!module.data_size) { \
606     RIME_STRUCT_INIT(RimeModule, module); \
607     module.module_name = #name; \
608     module.initialize = rime_##name##_initialize; \
609     module.finalize = rime_##name##_finalize; \
610     rime_customize_module_##name(&module); \
611   } \
612   rime_get_api()->register_module(&module); \
613 } \
614 static void rime_customize_module_##name(RimeModule* module)
615 
616 /*!
617  *  Defines a constant for a list of module names.
618  */
619 #define RIME_MODULE_LIST(var, ...) \
620 const char* var[] = { \
621   __VA_ARGS__, NULL \
622 } \
623 
624 /*!
625  *  Register a phony module which, when loaded, will load a list of modules.
626  *  \sa setup.cc for an example.
627  */
628 #define RIME_REGISTER_MODULE_GROUP(name, ...) \
629 static RIME_MODULE_LIST(rime_##name##_module_group, __VA_ARGS__); \
630 static void rime_##name##_initialize() { \
631   rime::LoadModules(rime_##name##_module_group); \
632 } \
633 static void rime_##name##_finalize() { \
634 } \
635 RIME_REGISTER_MODULE(name)
636 
637 #ifdef __cplusplus
638 }
639 #endif
640 
641 #endif  // RIME_API_H_
642