BRL-CAD
if_tk.c
Go to the documentation of this file.
1 /* I F _ T K . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2007-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @addtogroup if */
21 /** @{ */
22 /** @file if_tk.c
23  *
24  * Tk libfb interface.
25  *
26  */
27 /** @} */
28 
29 #include "common.h"
30 
31 #ifdef IF_TK
32 
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <string.h>
36 #ifdef HAVE_NETINET_IN_H
37 # include <netinet/in.h>
38 #endif
39 #include <tcl.h>
40 #include <tk.h>
41 
42 #include "bu/color.h"
43 #include "bu/log.h"
44 #include "bu/str.h"
45 
46 #include "fb_private.h"
47 #include "fb.h"
48 
49 
50 Tcl_Interp *fbinterp;
51 Tk_Window fbwin;
52 Tk_PhotoHandle fbphoto;
53 int p[2] = {0, 0};
54 
55 /* Note that Tk_PhotoPutBlock claims to have a faster
56  * copy method when pixelSize is 4 and alphaOffset is
57  * 3 - perhaps output could be massaged to generate this
58  * type of information and speed up the process?
59  *
60  * Might as well use one block and set the three things
61  * that actually change in tk_write
62  */
63 Tk_PhotoImageBlock block = {
64  NULL, /*Pointer to first pixel*/
65  0, /*Width of block in pixels*/
66  1, /*Height of block in pixels - always one for a scanline*/
67  0, /*Address difference between successive lines*/
68  3, /*Address difference between successive pixels on one scanline*/
69  {
70  RED,
71  GRN,
72  BLU,
73  0 /* alpha */
74  }
75 };
76 
77 
78 char *tkwrite_buffer;
79 
80 
81 HIDDEN int
82 fb_tk_open(fb *ifp, const char *file, int width, int height)
83 {
84  int pid = -1;
85  const char *cmd = "package require Tk";
86  char image_create_cmd[255] = {'\0'};
87  char canvas_create_cmd[255] = {'\0'};
88  char reportcolorcmd[255] = {'\0'};
89  const char canvas_pack_cmd[255] =
90  "pack .fb_tk_canvas -fill both -expand true";
91  const char place_image_cmd[255] =
92  ".fb_tk_canvas create image 0 0 -image fb_tk_photo -anchor nw";
93  const char *wmclosecmd = "wm protocol . WM_DELETE_WINDOW {set CloseWindow \"close\"}";
94  const char *bindclosecmd = "bind . <Button-3> {set CloseWindow \"close\"}";
95 
96  char *buffer;
97  char *linebuffer;
98 
99  FB_CK_FB(ifp);
100  if (file == (char *)NULL)
101  fb_log("fb_open(0x%lx, NULL, %d, %d)\n",
102  (unsigned long)ifp, width, height);
103  else
104  fb_log("fb_open(0x%lx, \"%s\", %d, %d)\n",
105  (unsigned long)ifp, file, width, height);
106 
107  /* check for default size */
108  if (width <= 0)
109  width = ifp->if_width;
110  if (height <= 0)
111  height = ifp->if_height;
112 
113  /* set debug bit vector */
114  if (file != NULL) {
115  const char *cp;
116  for (cp = file; *cp != '\0' && !isdigit((unsigned char)*cp); cp++)
117  ;
118  sscanf(cp, "%d", &ifp->if_debug);
119  } else {
120  ifp->if_debug = 0;
121  }
122 
123  /* Give the user whatever width was asked for */
124  ifp->if_width = width;
125  ifp->if_height = height;
126 
127  fbinterp = Tcl_CreateInterp();
128 
129  if (Tcl_Init(fbinterp) == TCL_ERROR) {
130  fb_log("Tcl_Init returned error in fb_open.");
131  }
132 
133  if (Tcl_Eval(fbinterp, cmd) != TCL_OK) {
134  fb_log("Error returned attempting to start tk in fb_open.");
135  }
136 
137  fbwin = Tk_MainWindow(fbinterp);
138 
139  Tk_GeometryRequest(fbwin, width, height);
140 
141  Tk_MakeWindowExist(fbwin);
142 
143  sprintf(image_create_cmd,
144  "image create photo fb_tk_photo -height %d -width %d",
145  width, height);
146 
147  if (Tcl_Eval(fbinterp, image_create_cmd) != TCL_OK) {
148  fb_log("Error returned attempting to create image in fb_open.");
149  }
150 
151  if ((fbphoto = Tk_FindPhoto(fbinterp, "fb_tk_photo")) == NULL) {
152  fb_log("Image creation unsuccessful in fb_open.");
153  }
154 
155  sprintf(canvas_create_cmd,
156  "canvas .fb_tk_canvas -highlightthickness 0 -height %d -width %d", width, height);
157 
158  sprintf (reportcolorcmd,
159  "bind . <Button-2> {puts \"At image (%%x, [expr %d - %%y]), real RGB = ([fb_tk_photo get %%x %%y])\n\"}", height);
160 
161  if (Tcl_Eval(fbinterp, canvas_create_cmd) != TCL_OK) {
162  fb_log("Error returned attempting to create canvas in fb_open.");
163  }
164 
165  if (Tcl_Eval(fbinterp, canvas_pack_cmd) != TCL_OK) {
166  fb_log("Error returned attempting to pack canvas in fb_open. %s",
167  Tcl_GetStringResult(fbinterp));
168  }
169 
170  if (Tcl_Eval(fbinterp, place_image_cmd) != TCL_OK) {
171  fb_log("Error returned attempting to place image in fb_open. %s",
172  Tcl_GetStringResult(fbinterp));
173  }
174 
175  /* Set our Tcl variable pertaining to whether a
176  * window closing event has been seen from the
177  * Window manager. WM_DELETE_WINDOW will be
178  * bound to a command setting this variable to
179  * the string "close", and a vwait watching
180  * for a change to the CloseWindow variable ensures
181  * a "lingering" tk window.
182  */
183  Tcl_SetVar(fbinterp, "CloseWindow", "open", 0);
184  if (Tcl_Eval(fbinterp, wmclosecmd) != TCL_OK) {
185  fb_log("Error binding WM_DELETE_WINDOW.");
186  }
187  if (Tcl_Eval(fbinterp, bindclosecmd) != TCL_OK) {
188  fb_log("Error binding right mouse button.");
189  }
190  if (Tcl_Eval(fbinterp, reportcolorcmd) != TCL_OK) {
191  fb_log("Error binding middle mouse button.");
192  }
193 
194  while (Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT));
195 
196  /* FIXME: malloc() is necessary here because there are callers
197  * that acquire a BU_SYM_SYSCALL semaphore for libfb calls.
198  * this should be investigated more closely to see if the
199  * semaphore acquires are critical or if they can be pushed
200  * down into libfb proper. in the meantime, manually call
201  * malloc()/free().
202  */
203  buffer = (char *)malloc(sizeof(uint32_t)*3+ifp->if_width*3);
204  linebuffer = (char *)malloc(ifp->if_width*3);
205  tkwrite_buffer = (char *)malloc(ifp->if_width*3);
206 
207  if (pipe(p) == -1) {
208  perror("pipe failed");
209  }
210 
211  pid = fork();
212  if (pid < 0) {
213  printf("boo, something bad\n");
214  } else if (pid > 0) {
215  int line = 0;
216  uint32_t lines[3];
217  int i;
218  int y[2];
219  y[0] = 0;
220 
221  /* parent */
222  while (y[0] >= 0) {
223  int count;
224 
225  /* If the Tk window gets a close event, bail */
226  if (BU_STR_EQUAL(Tcl_GetVar(fbinterp, "CloseWindow", 0), "close")) {
227  free(buffer);
228  free(linebuffer);
229  free(tkwrite_buffer);
230  fclose(stdin);
231  printf("Close Window event\n");
232  Tcl_Eval(fbinterp, "destroy .");
233  bu_exit(0, NULL);
234  }
235 
236  /* Unpack inputs from pipe */
237  count = read(p[0], buffer, sizeof(uint32_t)*3+ifp->if_width*3);
238  memcpy(lines, buffer, sizeof(uint32_t)*3);
239  memcpy(linebuffer, buffer+sizeof(uint32_t)*3, ifp->if_width*3);
240  y[0] = ntohl(lines[0]);
241  count = ntohl(lines[1]);
242 
243  if (y[0] < 0) {
244  break;
245  }
246 
247  line++;
248  block.pixelPtr = (unsigned char *)linebuffer;
249  block.width = count;
250  block.pitch = 3 * ifp->if_width;
251 
252 #if defined(TK_MAJOR_VERSION) && TK_MAJOR_VERSION >= 8 && defined(TK_MINOR_VERSION) && TK_MINOR_VERSION >= 5
253  Tk_PhotoPutBlock(fbinterp, fbphoto, &block, 0, ifp->if_height-y[0], count, 1, TK_PHOTO_COMPOSITE_SET);
254 #else
255  Tk_PhotoPutBlock(fbinterp, &block, 0, ifp->if_height-y[0], count, 1, TK_PHOTO_COMPOSITE_SET);
256 #endif
257 
258  do {
259  i = Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);
260  } while (i);
261  }
262  /* very bad things will happen if the parent does not terminate here */
263  free(buffer);
264  free(linebuffer);
265  free(tkwrite_buffer);
266  fclose(stdin);
267  Tcl_Eval(fbinterp, "vwait CloseWindow");
268  if (BU_STR_EQUAL(Tcl_GetVar(fbinterp, "CloseWindow", 0), "close")) {
269  printf("Close Window event\n");
270  Tcl_Eval(fbinterp, "destroy .");
271  }
272  bu_exit(0, NULL);
273  } else {
274  /* child */
275  printf("IMA CHILD\n");
276  fflush(stdout);
277  }
278 
279  return 0;
280 }
281 
283 tk_get_fbps(uint32_t UNUSED(magic))
284 {
285  return NULL;
286 }
287 
288 
289 HIDDEN void
290 tk_put_fbps(struct fb_platform_specific *UNUSED(fbps))
291 {
292  return;
293 }
294 
295 HIDDEN int
296 tk_open_existing(fb *UNUSED(ifp), int UNUSED(width), int UNUSED(height), struct fb_platform_specific *UNUSED(fb_p))
297 {
298  return 0;
299 }
300 
301 HIDDEN int
302 tk_close_existing(fb *UNUSED(ifp))
303 {
304  return 0;
305 }
306 
307 HIDDEN int
308 tk_configure_window(fb *UNUSED(ifp), int UNUSED(width), int UNUSED(height))
309 {
310  return 0;
311 }
312 
313 HIDDEN int
314 tk_refresh(fb *UNUSED(ifp), int UNUSED(x), int UNUSED(y), int UNUSED(w), int UNUSED(h))
315 {
316  return 0;
317 }
318 
319 HIDDEN int
320 fb_tk_close(fb *ifp)
321 {
322  int y[2];
323  int ret;
324  y[0] = -1;
325  y[1] = 0;
326  printf("Entering fb_tk_close\n");
327  FB_CK_FB(ifp);
328  ret = write(p[1], y, sizeof(y));
329  close(p[1]);
330  printf("Sent write (ret=%d) from fb_tk_close\n", ret);
331  return 0;
332 }
333 
334 
335 HIDDEN int
336 tk_clear(fb *ifp, unsigned char *pp)
337 {
338  FB_CK_FB(ifp);
339  if (pp == 0)
340  fb_log("fb_clear(0x%lx, NULL)\n", (unsigned long)ifp);
341  else
342  fb_log("fb_clear(0x%lx, &[%d %d %d])\n",
343  (unsigned long)ifp,
344  (int)(pp[RED]), (int)(pp[GRN]),
345  (int)(pp[BLU]));
346  return 0;
347 }
348 
349 
351 tk_read(fb *ifp, int x, int y, unsigned char *pixelp, size_t count)
352 {
353  FB_CK_FB(ifp);
354  fb_log("fb_read(0x%lx, %4d, %4d, 0x%lx, %ld)\n",
355  (unsigned long)ifp, x, y,
356  (unsigned long)pixelp, (long)count);
357  return (ssize_t)count;
358 }
359 
360 
362 tk_write(fb *ifp, int UNUSED(x), int y, const unsigned char *pixelp, size_t count)
363 {
364  uint32_t line[3];
365 
366  FB_CK_FB(ifp);
367  /* Set local values of Tk_PhotoImageBlock */
368  block.pixelPtr = (unsigned char *)pixelp;
369  block.width = count;
370  block.pitch = 3 * ifp->if_width;
371 
372  /* Pack values to be sent to parent */
373  line[0] = htonl(y);
374  line[1] = htonl((long)count);
375  line[2] = 0;
376 
377  memcpy(tkwrite_buffer, line, sizeof(uint32_t)*3);
378  memcpy(tkwrite_buffer+sizeof(uint32_t)*3, block.pixelPtr, 3 * ifp->if_width);
379 
380  /* Send values and data to parent for display */
381  if (write(p[1], tkwrite_buffer, 3 * ifp->if_width + 3*sizeof(uint32_t)) == -1) {
382  perror("Unable to write to pipe");
383  sleep(1);
384  }
385 
386  return (ssize_t)count;
387 }
388 
389 
390 HIDDEN int
391 tk_rmap(fb *ifp, ColorMap *cmp)
392 {
393  FB_CK_FB(ifp);
394  fb_log("fb_rmap(0x%lx, 0x%lx)\n",
395  (unsigned long)ifp, (unsigned long)cmp);
396  return 0;
397 }
398 
399 
400 HIDDEN int
401 tk_wmap(fb *ifp, const ColorMap *cmp)
402 {
403  int i;
404 
405  FB_CK_FB(ifp);
406  if (cmp == NULL)
407  fb_log("fb_wmap(0x%lx, NULL)\n",
408  (unsigned long)ifp);
409  else
410  fb_log("fb_wmap(0x%lx, 0x%lx)\n",
411  (unsigned long)ifp, (unsigned long)cmp);
412 
413  if (ifp->if_debug & FB_DEBUG_CMAP && cmp != NULL) {
414  for (i = 0; i < 256; i++) {
415  fb_log("%3d: [ 0x%4lx, 0x%4lx, 0x%4lx ]\n",
416  i,
417  (unsigned long)cmp->cm_red[i],
418  (unsigned long)cmp->cm_green[i],
419  (unsigned long)cmp->cm_blue[i]);
420  }
421  }
422 
423  return 0;
424 }
425 
426 
427 HIDDEN int
428 tk_view(fb *ifp, int xcenter, int ycenter, int xzoom, int yzoom)
429 {
430  FB_CK_FB(ifp);
431  fb_log("fb_view(0x%lx, %4d, %4d, %4d, %4d)\n",
432  (unsigned long)ifp, xcenter, ycenter, xzoom, yzoom);
433  fb_sim_view(ifp, xcenter, ycenter, xzoom, yzoom);
434  return 0;
435 }
436 
437 
438 HIDDEN int
439 tk_getview(fb *ifp, int *xcenter, int *ycenter, int *xzoom, int *yzoom)
440 {
441  FB_CK_FB(ifp);
442  fb_log("fb_getview(0x%lx, 0x%x, 0x%x, 0x%x, 0x%x)\n",
443  (unsigned long)ifp, xcenter, ycenter, xzoom, yzoom);
444  fb_sim_getview(ifp, xcenter, ycenter, xzoom, yzoom);
445  fb_log(" <= %d %d %d %d\n",
446  *xcenter, *ycenter, *xzoom, *yzoom);
447  return 0;
448 }
449 
450 
451 HIDDEN int
452 tk_setcursor(fb *ifp, const unsigned char *bits, int xbits, int ybits, int xorig, int yorig)
453 {
454  FB_CK_FB(ifp);
455  fb_log("fb_setcursor(0x%lx, 0x%lx, %d, %d, %d, %d)\n",
456  (unsigned long)ifp, bits, xbits, ybits, xorig, yorig);
457  return 0;
458 }
459 
460 
461 HIDDEN int
462 tk_cursor(fb *ifp, int mode, int x, int y)
463 {
464  fb_log("fb_cursor(0x%lx, %d, %4d, %4d)\n",
465  (unsigned long)ifp, mode, x, y);
466  fb_sim_cursor(ifp, mode, x, y);
467  return 0;
468 }
469 
470 
471 HIDDEN int
472 tk_getcursor(fb *ifp, int *mode, int *x, int *y)
473 {
474  FB_CK_FB(ifp);
475  fb_log("fb_getcursor(0x%lx, 0x%x, 0x%x, 0x%x)\n",
476  (unsigned long)ifp, mode, x, y);
477  fb_sim_getcursor(ifp, mode, x, y);
478  fb_log(" <= %d %d %d\n", *mode, *x, *y);
479  return 0;
480 }
481 
482 
483 HIDDEN int
484 tk_readrect(fb *ifp, int xmin, int ymin, int width, int height, unsigned char *pp)
485 {
486  FB_CK_FB(ifp);
487  fb_log("fb_readrect(0x%lx, (%4d, %4d), %4d, %4d, 0x%lx)\n",
488  (unsigned long)ifp, xmin, ymin, width, height,
489  (unsigned long)pp);
490  return width*height;
491 }
492 
493 
494 HIDDEN int
495 tk_writerect(fb *ifp, int xmin, int ymin, int width, int height, const unsigned char *pp)
496 {
497  FB_CK_FB(ifp);
498  fb_log("fb_writerect(0x%lx, %4d, %4d, %4d, %4d, 0x%lx)\n",
499  (unsigned long)ifp, xmin, ymin, width, height,
500  (unsigned long)pp);
501  return width*height;
502 }
503 
504 
505 HIDDEN int
506 tk_bwreadrect(fb *ifp, int xmin, int ymin, int width, int height, unsigned char *pp)
507 {
508  FB_CK_FB(ifp);
509  fb_log("fb_bwreadrect(0x%lx, (%4d, %4d), %4d, %4d, 0x%lx)\n",
510  (unsigned long)ifp, xmin, ymin, width, height,
511  (unsigned long)pp);
512  return width*height;
513 }
514 
515 
516 HIDDEN int
517 tk_bwwriterect(fb *ifp, int xmin, int ymin, int width, int height, const unsigned char *pp)
518 {
519  FB_CK_FB(ifp);
520  fb_log("fb_bwwriterect(0x%lx, %4d, %4d, %4d, %4d, 0x%lx)\n",
521  (unsigned long)ifp, xmin, ymin, width, height,
522  (unsigned long)pp);
523  return width*height;
524 }
525 
526 
527 HIDDEN int
528 tk_poll(fb *ifp)
529 {
530  FB_CK_FB(ifp);
531  while (Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT));
532  fb_log("fb_poll(0x%lx)\n", (unsigned long)ifp);
533  return 0;
534 }
535 
536 
537 HIDDEN int
538 tk_flush(fb *ifp)
539 {
540  FB_CK_FB(ifp);
541  fb_log("if_flush(0x%lx)\n", (unsigned long)ifp);
542  return 0;
543 }
544 
545 
546 HIDDEN int
547 tk_free(fb *ifp)
548 {
549  FB_CK_FB(ifp);
550  fb_log("fb_free(0x%lx)\n", (unsigned long)ifp);
551  return 0;
552 }
553 
554 
555 /*ARGSUSED*/
556 HIDDEN int
557 tk_help(fb *ifp)
558 {
559  FB_CK_FB(ifp);
560  fb_log("Description: %s\n", tk_interface.if_type);
561  fb_log("Device: %s\n", ifp->if_name);
562  fb_log("Max width/height: %d %d\n",
563  tk_interface.if_max_width,
564  tk_interface.if_max_height);
565  fb_log("Default width/height: %d %d\n",
566  tk_interface.if_width,
567  tk_interface.if_height);
568  fb_log("\
569 Usage: /dev/tk[#]\n\
570  where # is a optional bit vector from:\n\
571  1 debug buffered I/O calls\n\
572  2 show colormap entries in rmap/wmap calls\n\
573  4 show actual pixel values in read/write calls\n");
574  /*8 buffered read/write values - ifdef'd out*/
575 
576  return 0;
577 }
578 
579 /* This is the ONLY thing that we "export" */
580 fb tk_interface = {
581  0,
582  FB_TK_MAGIC,
583  fb_tk_open,
584  tk_open_existing,
585  tk_close_existing,
586  tk_get_fbps,
587  tk_put_fbps,
588  fb_tk_close,
589  tk_clear,
590  tk_read,
591  tk_write,
592  tk_rmap,
593  tk_wmap,
594  tk_view,
595  tk_getview,
596  tk_setcursor,
597  tk_cursor,
598  tk_getcursor,
599  tk_readrect,
600  tk_writerect,
601  tk_bwreadrect,
602  tk_bwwriterect,
603  tk_configure_window,
604  tk_refresh,
605  tk_poll,
606  tk_flush,
607  tk_free,
608  tk_help,
609  "Debugging Interface",
610  32*1024, /* max width */
611  32*1024, /* max height */
612  "/dev/tk",
613  512, /* current/default width */
614  512, /* current/default height */
615  -1, /* select fd */
616  -1, /* file descriptor */
617  1, 1, /* zoom */
618  256, 256, /* window center */
619  0, 0, 0, /* cursor */
620  PIXEL_NULL, /* page_base */
621  PIXEL_NULL, /* page_curp */
622  PIXEL_NULL, /* page_endp */
623  -1, /* page_no */
624  0, /* page_ref */
625  0L, /* page_curpos */
626  0L, /* page_pixels */
627  0, /* debug */
628  0, /* refresh rate */
629  {0}, /* u1 */
630  {0}, /* u2 */
631  {0}, /* u3 */
632  {0}, /* u4 */
633  {0}, /* u5 */
634  {0} /* u6 */
635 };
636 
637 
638 
639 #else
640 
641 /* quell empty-compilation unit warnings */
642 static const int unused = 0;
643 
644 #endif /* IF_TK */
645 
646 /*
647  * Local Variables:
648  * mode: C
649  * tab-width: 8
650  * indent-tabs-mode: t
651  * c-file-style: "stroustrup"
652  * End:
653  * ex: shiftwidth=4 tabstop=8
654  */
ptrdiff_t ssize_t
Definition: common.h:119
void fb_log(const char *fmt,...) _BU_ATTR_PRINTF12
Definition: fb_log.c:42
unsigned short cm_green[256]
Definition: fb.h:84
#define RED
Definition: color.h:39
#define PIXEL_NULL
Definition: fb.h:89
Header file for the BRL-CAD common definitions.
#define BLU
Definition: color.h:41
ustring width
#define HIDDEN
Definition: common.h:86
void bu_exit(int status, const char *fmt,...) _BU_ATTR_NORETURN _BU_ATTR_PRINTF23
Definition: bomb.c:195
#define FB_CK_FB(_p)
Definition: fb.h:100
int fb_sim_getcursor(fb *ifp, int *mode, int *x, int *y)
Definition: fb_util.c:96
unsigned short cm_red[256]
Definition: fb.h:83
#define FB_DEBUG_CMAP
Definition: fb.h:197
oldeumate l2 magic
Definition: nmg_mod.c:3843
#define UNUSED(parameter)
Definition: common.h:239
int fb_sim_cursor(fb *ifp, int mode, int x, int y)
Definition: fb_util.c:79
Definition: fb.h:82
void * Tk_Window
Definition: dm_xvars.h:44
int fb_sim_getview(fb *ifp, int *xcenter, int *ycenter, int *xzoom, int *yzoom)
Definition: fb_util.c:61
#define FB_TK_MAGIC
Definition: magic.h:184
int fb_sim_view(fb *ifp, int xcenter, int ycenter, int xzoom, int yzoom)
Definition: fb_util.c:43
A frame-buffer IO structure.
Definition: fb_private.h:80
int if_width
current values
Definition: fb_private.h:115
unsigned short cm_blue[256]
Definition: fb.h:85
#define GRN
Definition: color.h:40
char * if_name
what the user called it
Definition: fb_private.h:114
int if_debug
Buffered IO debug flag.
Definition: fb_private.h:134
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126