1 /* 2 * NOTE: This is a MODIFIED version of libvterm, see the README file. 3 */ 4 #ifndef __VTERM_H__ 5 #define __VTERM_H__ 6 7 #ifdef __cplusplus 8 extern "C" { 9 #endif 10 11 #include <stdlib.h> 12 13 #include "vterm_keycodes.h" 14 15 #define TRUE 1 16 #define FALSE 0 17 18 // from stdint.h 19 typedef unsigned char uint8_t; 20 typedef unsigned int uint32_t; 21 22 #define VTERM_VERSION_MAJOR 0 23 #define VTERM_VERSION_MINOR 1 24 25 #define VTERM_CHECK_VERSION \ 26 vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR) 27 28 typedef struct VTerm VTerm; 29 typedef struct VTermState VTermState; 30 typedef struct VTermScreen VTermScreen; 31 32 // Specifies a screen point. 33 typedef struct { 34 int row; 35 int col; 36 } VTermPos; 37 38 /* some small utility functions; we can just keep these static here */ 39 40 /* 41 * Order points by on-screen flow order: 42 * Return < 0 if "a" is before "b" 43 * Return 0 if "a" and "b" are equal 44 * Return > 0 if "a" is after "b". 45 */ 46 int vterm_pos_cmp(VTermPos a, VTermPos b); 47 48 #if defined(DEFINE_INLINES) || USE_INLINE 49 INLINE int vterm_pos_cmp(VTermPos a, VTermPos b) 50 { 51 return (a.row == b.row) ? a.col - b.col : a.row - b.row; 52 } 53 #endif 54 55 // Specifies a rectangular screen area. 56 typedef struct { 57 int start_row; 58 int end_row; 59 int start_col; 60 int end_col; 61 } VTermRect; 62 63 /* true if the rect contains the point */ 64 int vterm_rect_contains(VTermRect r, VTermPos p); 65 66 #if defined(DEFINE_INLINES) || USE_INLINE 67 INLINE int vterm_rect_contains(VTermRect r, VTermPos p) 68 { 69 return p.row >= r.start_row && p.row < r.end_row && 70 p.col >= r.start_col && p.col < r.end_col; 71 } 72 #endif 73 74 /* move a rect */ 75 // Move "rect" "row_delta" down and "col_delta" right. 76 // Does not check boundaries. 77 void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta); 78 79 #if defined(DEFINE_INLINES) || USE_INLINE 80 INLINE void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta) 81 { 82 rect->start_row += row_delta; rect->end_row += row_delta; 83 rect->start_col += col_delta; rect->end_col += col_delta; 84 } 85 #endif 86 87 /** 88 * Bit-field describing the value of VTermColor.type 89 */ 90 typedef enum { 91 /** 92 * If the lower bit of `type` is not set, the colour is 24-bit RGB. 93 */ 94 VTERM_COLOR_RGB = 0x00, 95 96 /** 97 * The colour is an index into a palette of 256 colours. 98 */ 99 VTERM_COLOR_INDEXED = 0x01, 100 101 /** 102 * Mask that can be used to extract the RGB/Indexed bit. 103 */ 104 VTERM_COLOR_TYPE_MASK = 0x01, 105 106 /** 107 * If set, indicates that this colour should be the default foreground 108 * color, i.e. there was no SGR request for another colour. When 109 * rendering this colour it is possible to ignore "idx" and just use a 110 * colour that is not in the palette. 111 */ 112 VTERM_COLOR_DEFAULT_FG = 0x02, 113 114 /** 115 * If set, indicates that this colour should be the default background 116 * color, i.e. there was no SGR request for another colour. A common 117 * option when rendering this colour is to not render a background at 118 * all, for example by rendering the window transparently at this spot. 119 */ 120 VTERM_COLOR_DEFAULT_BG = 0x04, 121 122 /** 123 * Mask that can be used to extract the default foreground/background bit. 124 */ 125 VTERM_COLOR_DEFAULT_MASK = 0x06 126 } VTermColorType; 127 128 /** 129 * Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the 130 * given VTermColor instance is an indexed colour. 131 */ 132 #define VTERM_COLOR_IS_INDEXED(col) \ 133 (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED) 134 135 /** 136 * Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that 137 * the given VTermColor instance is an rgb colour. 138 */ 139 #define VTERM_COLOR_IS_RGB(col) \ 140 (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB) 141 142 /** 143 * Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating 144 * that the given VTermColor instance corresponds to the default foreground 145 * color. 146 */ 147 #define VTERM_COLOR_IS_DEFAULT_FG(col) \ 148 (!!((col)->type & VTERM_COLOR_DEFAULT_FG)) 149 150 /** 151 * Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating 152 * that the given VTermColor instance corresponds to the default background 153 * color. 154 */ 155 #define VTERM_COLOR_IS_DEFAULT_BG(col) \ 156 (!!((col)->type & VTERM_COLOR_DEFAULT_BG)) 157 158 typedef struct { 159 /** 160 * Tag indicating which member is actually valid. 161 * Please use the `VTERM_COLOR_IS_*` test macros to check whether a 162 * particular type flag is set. 163 */ 164 uint8_t type; 165 166 uint8_t red, green, blue; 167 168 uint8_t index; 169 } VTermColor; 170 171 /** 172 * Constructs a new VTermColor instance representing the given RGB values. 173 */ 174 void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green, uint8_t blue); 175 176 /** 177 * Construct a new VTermColor instance representing an indexed color with the 178 * given index. 179 */ 180 void vterm_color_indexed(VTermColor *col, uint8_t idx); 181 182 /** 183 * Compares two colours. Returns true if the colors are equal, false otherwise. 184 */ 185 int vterm_color_is_equal(const VTermColor *a, const VTermColor *b); 186 187 typedef enum { 188 /* VTERM_VALUETYPE_NONE = 0 */ 189 VTERM_VALUETYPE_BOOL = 1, 190 VTERM_VALUETYPE_INT, 191 VTERM_VALUETYPE_STRING, 192 VTERM_VALUETYPE_COLOR, 193 194 VTERM_N_VALUETYPES 195 } VTermValueType; 196 197 typedef struct { 198 const char *str; 199 size_t len : 30; 200 unsigned int initial : 1; 201 unsigned int final : 1; 202 } VTermStringFragment; 203 204 typedef union { 205 int boolean; 206 int number; 207 VTermStringFragment string; 208 VTermColor color; 209 } VTermValue; 210 211 typedef enum { 212 /* VTERM_ATTR_NONE = 0 */ 213 VTERM_ATTR_BOLD = 1, // bool: 1, 22 214 VTERM_ATTR_UNDERLINE, // number: 4, 21, 24 215 VTERM_ATTR_ITALIC, // bool: 3, 23 216 VTERM_ATTR_BLINK, // bool: 5, 25 217 VTERM_ATTR_REVERSE, // bool: 7, 27 218 VTERM_ATTR_CONCEAL, // bool: 8, 28 219 VTERM_ATTR_STRIKE, // bool: 9, 29 220 VTERM_ATTR_FONT, // number: 10-19 221 VTERM_ATTR_FOREGROUND, // color: 30-39 90-97 222 VTERM_ATTR_BACKGROUND, // color: 40-49 100-107 223 224 VTERM_N_ATTRS 225 } VTermAttr; 226 227 typedef enum { 228 /* VTERM_PROP_NONE = 0 */ 229 VTERM_PROP_CURSORVISIBLE = 1, // bool 230 VTERM_PROP_CURSORBLINK, // bool 231 VTERM_PROP_ALTSCREEN, // bool 232 VTERM_PROP_TITLE, // string 233 VTERM_PROP_ICONNAME, // string 234 VTERM_PROP_REVERSE, // bool 235 VTERM_PROP_CURSORSHAPE, // number 236 VTERM_PROP_MOUSE, // number 237 VTERM_PROP_CURSORCOLOR, // string 238 239 VTERM_N_PROPS 240 } VTermProp; 241 242 enum { 243 VTERM_PROP_CURSORSHAPE_BLOCK = 1, 244 VTERM_PROP_CURSORSHAPE_UNDERLINE, 245 VTERM_PROP_CURSORSHAPE_BAR_LEFT, 246 247 VTERM_N_PROP_CURSORSHAPES 248 }; 249 250 enum { 251 VTERM_PROP_MOUSE_NONE = 0, 252 VTERM_PROP_MOUSE_CLICK, 253 VTERM_PROP_MOUSE_DRAG, 254 VTERM_PROP_MOUSE_MOVE, 255 256 VTERM_N_PROP_MOUSES 257 }; 258 259 typedef struct { 260 const uint32_t *chars; 261 int width; 262 unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */ 263 unsigned int dwl:1; /* DECDWL or DECDHL double-width line */ 264 unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */ 265 } VTermGlyphInfo; 266 267 typedef struct { 268 unsigned int doublewidth:1; /* DECDWL or DECDHL line */ 269 unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */ 270 unsigned int continuation:1; /* Line is a flow continuation of the previous */ 271 } VTermLineInfo; 272 273 /* Copies of VTermState fields that the 'resize' callback might have reason to 274 * edit. 'resize' callback gets total control of these fields and may 275 * free-and-reallocate them if required. They will be copied back from the 276 * struct after the callback has returned. 277 */ 278 typedef struct { 279 VTermPos pos; /* current cursor position */ 280 } VTermStateFields; 281 282 typedef struct { 283 /* libvterm relies on this memory to be zeroed out before it is returned 284 * by the allocator. */ 285 void *(*malloc)(size_t size, void *allocdata); 286 void (*free)(void *ptr, void *allocdata); 287 } VTermAllocatorFunctions; 288 289 void vterm_check_version(int major, int minor); 290 291 // Allocate and initialize a new terminal with default allocators. 292 VTerm *vterm_new(int rows, int cols); 293 294 // Allocate and initialize a new terminal with specified allocators. 295 VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata); 296 297 // Free and cleanup a terminal and all its data. 298 void vterm_free(VTerm* vt); 299 300 // Get the current size of the terminal and store in "rowsp" and "colsp". 301 void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp); 302 303 void vterm_set_size(VTerm *vt, int rows, int cols); 304 305 int vterm_get_utf8(const VTerm *vt); 306 void vterm_set_utf8(VTerm *vt, int is_utf8); 307 308 size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len); 309 310 /* Setting output callback will override the buffer logic */ 311 typedef void VTermOutputCallback(const char *s, size_t len, void *user); 312 void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user); 313 314 /* These buffer functions only work if output callback is NOT set 315 * These are deprecated and will be removed in a later version */ 316 size_t vterm_output_get_buffer_size(const VTerm *vt); 317 size_t vterm_output_get_buffer_current(const VTerm *vt); 318 size_t vterm_output_get_buffer_remaining(const VTerm *vt); 319 320 /* This too */ 321 size_t vterm_output_read(VTerm *vt, char *buffer, size_t len); 322 323 int vterm_is_modify_other_keys(VTerm *vt); 324 void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod); 325 void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod); 326 327 void vterm_keyboard_start_paste(VTerm *vt); 328 void vterm_keyboard_end_paste(VTerm *vt); 329 330 void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod); 331 // "button" is 1 for left, 2 for middle, 3 for right. 332 // Button 4 is scroll wheel down, button 5 is scroll wheel up. 333 void vterm_mouse_button(VTerm *vt, int button, int pressed, VTermModifier mod); 334 335 // ------------ 336 // Parser layer 337 // ------------ 338 339 /* Flag to indicate non-final subparameters in a single CSI parameter. 340 * Consider 341 * CSI 1;2:3:4;5a 342 * 1 4 and 5 are final. 343 * 2 and 3 are non-final and will have this bit set 344 * 345 * Don't confuse this with the final byte of the CSI escape; 'a' in this case. 346 */ 347 #define CSI_ARG_FLAG_MORE (1U<<31) 348 #define CSI_ARG_MASK (~(1U<<31)) 349 350 #define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE) 351 #define CSI_ARG(a) ((a) & CSI_ARG_MASK) 352 353 /* Can't use -1 to indicate a missing argument; use this instead */ 354 #define CSI_ARG_MISSING ((1<<30)-1) 355 356 #define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING) 357 #define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a)) 358 #define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a)) 359 360 typedef struct { 361 int (*text)(const char *bytes, size_t len, void *user); 362 int (*control)(unsigned char control, void *user); 363 int (*escape)(const char *bytes, size_t len, void *user); 364 int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user); 365 int (*osc)(int command, VTermStringFragment frag, void *user); 366 int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user); 367 int (*resize)(int rows, int cols, void *user); 368 } VTermParserCallbacks; 369 370 void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user); 371 void *vterm_parser_get_cbdata(VTerm *vt); 372 373 // ----------- 374 // State layer 375 // ----------- 376 377 typedef struct { 378 int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user); 379 int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user); 380 int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user); 381 int (*moverect)(VTermRect dest, VTermRect src, void *user); 382 int (*erase)(VTermRect rect, int selective, void *user); 383 int (*initpen)(void *user); 384 int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user); 385 // Callback for setting various properties. Must return 1 if the property 386 // was accepted, 0 otherwise. 387 int (*settermprop)(VTermProp prop, VTermValue *val, void *user); 388 int (*bell)(void *user); 389 int (*resize)(int rows, int cols, VTermStateFields *fields, void *user); 390 int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user); 391 } VTermStateCallbacks; 392 393 typedef struct { 394 VTermPos pos; 395 int buttons; 396 #define MOUSE_BUTTON_LEFT 0x01 397 #define MOUSE_BUTTON_MIDDLE 0x02 398 #define MOUSE_BUTTON_RIGHT 0x04 399 int flags; 400 #define MOUSE_WANT_CLICK 0x01 401 #define MOUSE_WANT_DRAG 0x02 402 #define MOUSE_WANT_MOVE 0x04 403 // useful to add protocol? 404 } VTermMouseState; 405 406 typedef struct { 407 int (*control)(unsigned char control, void *user); 408 int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user); 409 int (*osc)(int command, VTermStringFragment frag, void *user); 410 int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user); 411 } VTermStateFallbacks; 412 413 VTermState *vterm_obtain_state(VTerm *vt); 414 415 void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user); 416 void *vterm_state_get_cbdata(VTermState *state); 417 418 void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user); 419 void *vterm_state_get_unrecognised_fbdata(VTermState *state); 420 421 // Initialize the state. 422 void vterm_state_reset(VTermState *state, int hard); 423 424 void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos); 425 void vterm_state_get_mousestate(const VTermState *state, VTermMouseState *mousestate); 426 void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg); 427 void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col); 428 void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg); 429 void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col); 430 void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright); 431 int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val); 432 int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val); 433 void vterm_state_focus_in(VTermState *state); 434 void vterm_state_focus_out(VTermState *state); 435 const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row); 436 437 /** 438 * Makes sure that the given color `col` is indeed an RGB colour. After this 439 * function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other 440 * flags stored in `col->type` will have been reset. 441 * 442 * @param state is the VTermState instance from which the colour palette should 443 * be extracted. 444 * @param col is a pointer at the VTermColor instance that should be converted 445 * to an RGB colour. 446 */ 447 void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col); 448 449 // ------------ 450 // Screen layer 451 // ------------ 452 453 typedef struct { 454 unsigned int bold : 1; 455 unsigned int underline : 2; 456 unsigned int italic : 1; 457 unsigned int blink : 1; 458 unsigned int reverse : 1; 459 unsigned int conceal : 1; 460 unsigned int strike : 1; 461 unsigned int font : 4; /* 0 to 9 */ 462 unsigned int dwl : 1; /* On a DECDWL or DECDHL line */ 463 unsigned int dhl : 2; /* On a DECDHL line (1=top 2=bottom) */ 464 } VTermScreenCellAttrs; 465 466 enum { 467 VTERM_UNDERLINE_OFF, 468 VTERM_UNDERLINE_SINGLE, 469 VTERM_UNDERLINE_DOUBLE, 470 VTERM_UNDERLINE_CURLY, 471 }; 472 473 typedef struct { 474 #define VTERM_MAX_CHARS_PER_CELL 6 475 uint32_t chars[VTERM_MAX_CHARS_PER_CELL]; 476 char width; 477 VTermScreenCellAttrs attrs; 478 VTermColor fg, bg; 479 } VTermScreenCell; 480 481 // All fields are optional, NULL when not used. 482 typedef struct { 483 int (*damage)(VTermRect rect, void *user); 484 int (*moverect)(VTermRect dest, VTermRect src, void *user); 485 int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user); 486 int (*settermprop)(VTermProp prop, VTermValue *val, void *user); 487 int (*bell)(void *user); 488 int (*resize)(int rows, int cols, void *user); 489 // A line was pushed off the top of the window. 490 // "cells[cols]" contains the cells of that line. 491 // Return value is unused. 492 int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user); 493 int (*sb_popline)(int cols, VTermScreenCell *cells, void *user); 494 } VTermScreenCallbacks; 495 496 // Return the screen of the vterm. 497 VTermScreen *vterm_obtain_screen(VTerm *vt); 498 499 /* 500 * Install screen callbacks. These are invoked when the screen contents is 501 * changed. "user" is passed into to the callback. 502 */ 503 void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user); 504 void *vterm_screen_get_cbdata(VTermScreen *screen); 505 506 void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user); 507 void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen); 508 509 // Enable support for using the alternate screen if "altscreen" is non-zero. 510 // Before that switching to the alternate screen won't work. 511 // Calling with "altscreen" zero has no effect. 512 void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen); 513 514 typedef enum { 515 VTERM_DAMAGE_CELL, /* every cell */ 516 VTERM_DAMAGE_ROW, /* entire rows */ 517 VTERM_DAMAGE_SCREEN, /* entire screen */ 518 VTERM_DAMAGE_SCROLL, /* entire screen + scrollrect */ 519 520 VTERM_N_DAMAGES 521 } VTermDamageSize; 522 523 // Invoke the relevant callbacks to update the screen. 524 void vterm_screen_flush_damage(VTermScreen *screen); 525 526 void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size); 527 528 /* 529 * Reset the screen. Also invokes vterm_state_reset(). 530 * Must be called before the terminal can be used. 531 */ 532 void vterm_screen_reset(VTermScreen *screen, int hard); 533 534 /* Neither of these functions NUL-terminate the buffer */ 535 size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect); 536 size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect); 537 538 typedef enum { 539 VTERM_ATTR_BOLD_MASK = 1 << 0, 540 VTERM_ATTR_UNDERLINE_MASK = 1 << 1, 541 VTERM_ATTR_ITALIC_MASK = 1 << 2, 542 VTERM_ATTR_BLINK_MASK = 1 << 3, 543 VTERM_ATTR_REVERSE_MASK = 1 << 4, 544 VTERM_ATTR_STRIKE_MASK = 1 << 5, 545 VTERM_ATTR_FONT_MASK = 1 << 6, 546 VTERM_ATTR_FOREGROUND_MASK = 1 << 7, 547 VTERM_ATTR_BACKGROUND_MASK = 1 << 8, 548 VTERM_ATTR_CONCEAL_MASK = 1 << 9, 549 550 VTERM_ALL_ATTRS_MASK = (1 << 10) - 1 551 } VTermAttrMask; 552 553 int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs); 554 555 int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell); 556 557 int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos); 558 559 /** 560 * Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state` 561 * instance. 562 */ 563 void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col); 564 565 // --------- 566 // Utilities 567 // --------- 568 569 VTermValueType vterm_get_attr_type(VTermAttr attr); 570 VTermValueType vterm_get_prop_type(VTermProp prop); 571 572 void vterm_scroll_rect(VTermRect rect, 573 int downward, 574 int rightward, 575 int (*moverect)(VTermRect src, VTermRect dest, void *user), 576 int (*eraserect)(VTermRect rect, int selective, void *user), 577 void *user); 578 579 void vterm_copy_cells(VTermRect dest, 580 VTermRect src, 581 void (*copycell)(VTermPos dest, VTermPos src, void *user), 582 void *user); 583 584 #ifdef __cplusplus 585 } 586 #endif 587 588 #endif 589