1 /*
2 This file is part of GNU APL, a free implementation of the
3 ISO/IEC Standard 13751, "Programming Language APL, Extended"
4
5 Copyright (C) 2008-2018 Dr. Jürgen Sauermann
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <poll.h>
24 #include <sys/ioctl.h>
25
26 #include "../config.h"
27 #include "LibPaths.hh"
28 #include "PointerCell.hh"
29 #include "Quad_GTK.hh"
30 #include "Workspace.hh"
31
32 Quad_GTK Quad_GTK::_fun;
33 Quad_GTK * Quad_GTK::fun = &Quad_GTK::_fun;
34
35 #if HAVE_GTK3
36 //-----------------------------------------------------------------------------
37 Token
eval_AB(Value_P A,Value_P B)38 Quad_GTK::eval_AB(Value_P A, Value_P B)
39 {
40 if (A->get_rank() > 1) RANK_ERROR;
41 if (B->get_rank() > 1) RANK_ERROR;
42 if (A->is_char_array() && B->is_char_array())
43 {
44 const UCS_string css_filename = A->get_UCS_ravel();
45 const UCS_string gui_filename = B->get_UCS_ravel();
46 const int fd = open_window(gui_filename, &css_filename);
47 return Token(TOK_APL_VALUE1, IntScalar(fd, LOC));
48 }
49
50 if (!B->is_int_scalar())
51 {
52 MORE_ERROR() << "A ⎕GTK B expects B to be an integer scalar"
53 " or a text vector";
54 DOMAIN_ERROR;
55 }
56
57 const int function = B->get_ravel(0).get_int_value();
58 int fd = -1;
59 switch(function)
60 {
61 case 0: // close window/GUI
62 if (!A->is_int_scalar()) goto bad_fd;
63 fd = A->get_ravel(0).get_int_value();
64 return Token(TOK_APL_VALUE1, close_window(fd));
65
66 case 3: // increase verbosity
67 if (!A->is_int_scalar()) goto bad_fd;
68 if (write_TL0(fd, 7))
69 {
70 CERR << "write( Tag 7) failed in Ah ⎕GTK 3";
71 return Token(TOK_APL_VALUE1, IntScalar(-3, LOC));
72 }
73 return Token(TOK_APL_VALUE1, IntScalar(0, LOC));
74
75 case 4: // decrease verbosity
76 if (!A->is_int_scalar()) goto bad_fd;
77 if (write_TL0(fd, 8))
78 {
79 CERR << "write( Tag 8) failed in Ah ⎕GTK 3";
80 return Token(TOK_APL_VALUE1, IntScalar(-4, LOC));
81 }
82 return Token(TOK_APL_VALUE1, IntScalar(0, LOC));
83
84 default: MORE_ERROR() << "Invalid function number Bi=" << function
85 << " in A ⎕GTK Bi";
86 DOMAIN_ERROR;
87 }
88
89 MORE_ERROR() << "Unexpected A or B in A ⎕GTK B";
90 DOMAIN_ERROR;
91
92 bad_fd:
93 MORE_ERROR() << "Ah ⎕GTK " << function
94 << " expects a handle (i.e. an integer scalar) Ah";
95 DOMAIN_ERROR;
96 }
97 //-----------------------------------------------------------------------------
98 Token
eval_B(Value_P B)99 Quad_GTK::eval_B(Value_P B)
100 {
101 if (B->get_rank() > 1) RANK_ERROR;
102 if (B->is_char_array())
103 {
104 const UCS_string gui_filename = B->get_UCS_ravel();
105 const int fd = open_window(gui_filename, /* no CSS */ 0);
106 return Token(TOK_APL_VALUE1, IntScalar(fd, LOC));
107 }
108
109 if (!B->is_int_scalar())
110 {
111 MORE_ERROR() << "⎕GTK B expects B to be an integer scalar"
112 " or a text vector";
113 DOMAIN_ERROR;
114 }
115
116 const int function = B->get_ravel(0).get_int_value();
117 switch(function)
118 {
119 case 0: // list of open fds
120 return Token(TOK_APL_VALUE1, window_list());
121
122 case 1: // blocking poll for next event
123 case 2: // non-blocking wait for next event
124 poll_all();
125 if (function == 2 && event_queue.size() == 0) // non-blocking
126 return Token(TOK_APL_VALUE1, IntScalar(0, LOC));
127
128 // blocking
129 //
130 while (event_queue.size() == 0 && !interrupt_is_raised())
131 poll_all();
132
133 // at this point either event_queue.size() > 0 or an interrupt
134 // was raised
135 //
136 if (event_queue.size() == 0) // hence interrupt was raised
137 {
138 clear_interrupt_raised(LOC);
139 return Token(TOK_APL_VALUE1, IntScalar(0, LOC));
140 }
141
142 {
143 UCS_string HWF = event_queue[0]; // fd, widget : fun
144 event_queue.erase(event_queue.begin());
145
146 // split (Unicode)Handle,"widget:function"
147 // into a 3-element APL vector Handle (⊂"widget") (⊂"function")
148 //
149 UCS_string_vector args;
150 UCS_string arg;
151 for (ShapeItem j = 1; j < HWF.size(); ++j)
152 {
153 if (HWF[j] == UNI_ASCII_COLON)
154 {
155 args.push_back(arg);
156 arg.clear();
157 }
158 else
159 {
160 arg.append(HWF[j]);
161 }
162 }
163 args.push_back(arg);
164
165 Value_P Z(1 + args.size(), LOC);
166 new (Z->next_ravel()) IntCell(HWF[0]);
167 loop(a, args.size())
168 {
169 Value_P Za(args[a], LOC);
170 new (Z->next_ravel()) PointerCell(Za.get(), Z.getref());
171 }
172 Z->check_value(LOC);
173 return Token(TOK_APL_VALUE1, Z);
174 }
175
176 default: MORE_ERROR() << "Invalid function number Bi=" << function
177 << " in ⎕GTK Bi";
178 DOMAIN_ERROR;
179 }
180
181 MORE_ERROR() << "Unexpected B in ⎕GTK B";
182 DOMAIN_ERROR;
183 }
184 //-----------------------------------------------------------------------------
185 Token
eval_AXB(Value_P A,Value_P X,Value_P B)186 Quad_GTK::eval_AXB(Value_P A, Value_P X, Value_P B)
187 {
188 if (B->get_rank() > 1) RANK_ERROR;
189
190 UTF8_string window_id; // e.g. "entry1"
191 const int fd = resolve_window(X.get(), window_id);
192 write_TLV(fd, 6, window_id); // select wodget
193
194 int fun = FNUM_INVALID;
195 if (B->is_int_scalar()) fun = B->get_ravel(0).get_int_value();
196 else if (B->is_char_string()) fun = resolve_fun_name(window_id, B.get());
197 else
198 {
199 MORE_ERROR() << "A ⎕GTK[X] B expects B to be an integer scalar"
200 " or a text vector";
201 DOMAIN_ERROR;
202 }
203
204 int command_tag = -1;
205 int response_tag = -1;
206 Gtype Atype = gtype_V;
207
208 switch(fun)
209 {
210 case FNUM_INVALID: DOMAIN_ERROR;
211 case FNUM_0: return Token(TOK_APL_VALUE1, IntScalar(fd, LOC));
212
213 #define gtk_event_def(ev_ename, ...)
214 #define gtk_fun_def(glade_ID, gtk_class, gtk_function, _ZAname,_Z, A,_help) \
215 case FNUM_ ## gtk_class ## _ ## gtk_function: \
216 command_tag = Command_ ## gtk_class ## _ ## gtk_function; \
217 response_tag = Response_ ## gtk_class ## _ ## gtk_function; \
218 Atype = gtype_ ## A; \
219 break;
220 #include "Gtk/Gtk_map.def"
221
222 default:
223 MORE_ERROR() << "Bad function B in A ⎕GTK B (B='"
224 << *B << ", fun=" << fun;
225 DOMAIN_ERROR;
226 }
227
228 if (Atype == gtype_V) VALENCE_ERROR;
229 if (A->get_rank() > 1) RANK_ERROR;
230
231 UCS_string ucs_A;
232 if (fun == FNUM_GtkDrawingArea_draw_commands)
233 {
234 loop(a, A->element_count())
235 {
236 const Cell & cell = A->get_ravel(a);
237 if (!cell.is_pointer_cell())
238 {
239 MORE_ERROR() << "A ⎕GTK " << fun
240 << " expects A to be a vector of "
241 "draw commands (strings)";
242 DOMAIN_ERROR;
243 }
244 Value_P command = cell.get_pointer_value();
245 ucs_A.append(UCS_string(*command));
246 ucs_A.append(UNI_ASCII_LF);
247 }
248 }
249 else
250 {
251 if (!A->is_char_string())
252 {
253 MORE_ERROR() << "A ⎕GTK[X] B expects A to be a text vector";
254 DOMAIN_ERROR;
255 }
256
257 ucs_A = UCS_string(*A);
258 }
259 UTF8_string utf_A(ucs_A);
260 write_TLV(fd, command_tag, utf_A);
261 return Token(TOK_APL_VALUE1, poll_response(fd, response_tag));
262 }
263 //-----------------------------------------------------------------------------
264 Token
eval_XB(Value_P X,Value_P B)265 Quad_GTK::eval_XB(Value_P X, Value_P B)
266 {
267 if (B->get_rank() > 1) RANK_ERROR;
268
269 UTF8_string window_id; // e.g. "entry1"
270 const int fd = resolve_window(X.get(), window_id);
271 write_TLV(fd, 6, window_id); // select wodget
272
273 int fun = FNUM_INVALID;
274 if (B->is_int_scalar()) fun = B->get_ravel(0).get_int_value();
275 else if (B->is_char_string()) fun = resolve_fun_name(window_id, B.get());
276 else DOMAIN_ERROR;
277
278 int command_tag = -1;
279 int response_tag = -1;
280 Gtype Atype = gtype_V;
281
282 switch(fun)
283 {
284 case 0:
285 write_TLV(fd, 4, window_id);
286 return Token(TOK_APL_VALUE1, IntScalar(fd, LOC));
287
288 #define gtk_event_def(ev_ename, ...)
289 #define gtk_fun_def(glade_ID, gtk_class, gtk_function, _ZAname, _Z, A, _help) \
290 case FNUM_ ## gtk_class ## _ ## gtk_function: \
291 command_tag = Command_ ## gtk_class ## _ ## gtk_function; \
292 response_tag = Response_ ## gtk_class ## _ ## gtk_function; \
293 Atype = gtype_ ## A; \
294 break;
295 #include "Gtk/Gtk_map.def"
296 }
297
298 if (Atype != gtype_V) VALENCE_ERROR;
299
300 write_TL0(fd, command_tag); // command
301 return Token(TOK_APL_VALUE1, poll_response(fd, response_tag));
302 }
303 //-----------------------------------------------------------------------------
304 Value_P
read_fd(int fd,int tag)305 Quad_GTK::read_fd(int fd, int tag)
306 {
307 // tag == -1 indicates a poll for ANY tag (= an event as opposed to a
308 // command response). In that case the poll is non-blocking.
309
310 char TLV[1000];
311
312 const ssize_t rx_len = read(fd, TLV, sizeof(TLV));
313
314 if (rx_len < 8)
315 {
316 MORE_ERROR() << "read() failed in Quad_GTK::read_fd(): "
317 << strerror(errno);
318 DOMAIN_ERROR;
319 }
320
321 const int TLV_tag = (TLV[0] & 0xFF) << 24 | (TLV[1] & 0xFF) << 16
322 | (TLV[2] & 0xFF) << 8 | (TLV[3] & 0xFF);
323
324
325 const int V_len = (TLV[4] & 0xFF) << 24 | (TLV[5] & 0xFF) << 16
326 | (TLV[6] & 0xFF) << 8 | (TLV[7] & 0xFF);
327
328 Assert(rx_len == (V_len + 8));
329
330 char * V = TLV + 8;
331 V[V_len] = 0;
332
333 // 1. check for events from generic_callback()
334 //
335 if (TLV_tag == Event_widget_fun || // "H:button1:clicked"
336 TLV_tag == Event_widget_fun_id_class || // dito + :id:class
337 TLV_tag == Event_toplevel_window_done)
338 {
339 // V is a string of the form H%s:%s:%s:% where H is a placeholder
340 // for the fd over which we have received V.
341 //
342 // replace H by the actual fd and store the result in event_queue.
343 //
344 UTF8_string data_utf; // H:widget:callback
345 loop(v, V_len) data_utf += V[v];
346 UCS_string data_ucs(data_utf);
347 data_ucs[0] = (Unicode(fd));
348
349 event_queue.push_back(data_ucs);
350
351 return Value_P(); // i.e. NULL
352 }
353
354 if (tag == -1) // not waiting for a specific tag
355 {
356 CERR << "*** ⎕GTK: ignoring event: " << V << endl;
357 return Value_P(); // i.e. NULL
358 }
359
360 if (TLV_tag != tag)
361 {
362 CERR << "Got unexpected tag " << TLV_tag
363 << " when waiting for response " << tag << endl;
364 return Value_P();
365 }
366
367 // TLV_tag is the expected one. Strip off the response offset
368 // Response_0 (= 2000) from the TLV_tag to get the function number
369 // and return APL vector function,"result-value"
370 //
371 UTF8_string utf;
372 loop(u, V_len) utf += V[u];
373 UCS_string ucs(utf);
374
375 Value_P Z(1 + ucs.size(), LOC);
376 new (Z->next_ravel()) IntCell(TLV_tag - Response_0);
377 loop(u, ucs.size()) new (Z->next_ravel()) CharCell(ucs[u]);
378 Z->check_value(LOC);
379 return Z;
380 }
381 //-----------------------------------------------------------------------------
382 void
poll_all()383 Quad_GTK::poll_all()
384 {
385 const int count = open_windows.size();
386 pollfd fds[count];
387 loop(w, count)
388 {
389 fds[w].fd = open_windows[w].fd;
390 fds[w].events = POLLIN | POLLHUP;
391 fds[w].revents = 0;
392 }
393
394 const int ready = poll(fds, count, 0);
395 if (ready == 0) return;
396 if (ready > 0)
397 {
398 loop(w, count)
399 {
400 if (fds[w].revents) read_fd(fds[w].fd, -1);
401 }
402 }
403 else
404 {
405 if (errno == EINTR) return; // user hits ^C
406 CERR << "*** poll() failed: " << strerror(errno) << endl;
407 }
408 }
409 //-----------------------------------------------------------------------------
410 Value_P
poll_response(int fd,int tag)411 Quad_GTK::poll_response(int fd, int tag)
412 {
413 again:
414
415 pollfd pfd;
416 pfd.fd = fd;
417 pfd.events = POLLIN | POLLHUP;
418 pfd.revents = 0;
419
420 const int ready = poll(&pfd, 1, 0);
421 if (ready == 0)
422 {
423 usleep(100000);
424 goto again;
425 }
426
427 if (ready > 0 && pfd.revents)
428 {
429 Value_P Z = read_fd(fd, tag);
430 if (!Z) goto again;
431 return Z;
432 }
433
434 CERR << "*** poll() failed" << endl;
435 DOMAIN_ERROR;
436 }
437 //-----------------------------------------------------------------------------
438 Value_P
window_list() const439 Quad_GTK::window_list() const
440 {
441 Value_P Z(open_windows.size(), LOC);
442 loop(w, open_windows.size())
443 {
444 const APL_Integer fd = open_windows[w].fd;
445 new (Z->next_ravel()) IntCell(fd);
446 }
447
448 Z->set_default_Int();
449 Z->check_value(LOC);
450 return Z;
451 }
452 //-----------------------------------------------------------------------------
453 int
resolve_window(const Value * X,UTF8_string & window_id)454 Quad_GTK::resolve_window(const Value * X, UTF8_string & window_id)
455 {
456 if (X->get_rank() > 1) RANK_ERROR;
457 const int fd = X->get_ravel(0).get_int_value();
458
459 // verify that ↑X is an open window...
460 //
461 bool window_valid = false;
462 loop(w, open_windows.size())
463 {
464 if (fd == open_windows[w].fd)
465 {
466 window_valid = true;
467 break;
468 }
469 }
470
471 if (!window_valid)
472 {
473 MORE_ERROR() << "Invalid window " << fd
474 << " in Quad_GTK::resolve_window()";
475 DOMAIN_ERROR;
476 }
477
478 loop(i, X->element_count())
479 {
480 if (i) window_id += X->get_ravel(i).get_char_value();
481 }
482
483 return fd;
484 }
485 //-----------------------------------------------------------------------------
486 Quad_GTK::Fnum
resolve_fun_name(UTF8_string & window_id,const Value * B)487 Quad_GTK::resolve_fun_name(UTF8_string & window_id, const Value * B)
488 {
489 // window_id is a class and an instance number, such as entry1
490 // Determine the length of the class prefixx
491 //
492 int wid_len = window_id.size();
493 while (wid_len &&
494 window_id[wid_len - 1] >= '0' &&
495 window_id[wid_len - 1] <= '9') --wid_len;
496
497 const UCS_string ucs_B(*B);
498 UTF8_string utf_B(ucs_B);
499 const char * wid_class = window_id.c_str();
500 const char * fun_name = utf_B.c_str();
501
502 #define gtk_event_def(ev_ename, ...)
503 #define gtk_fun_def(glade_ID, gtk_class, gtk_function, _ZAname,_Z,_A,_help) \
504 if (!(strncmp(wid_class, #glade_ID, wid_len) || \
505 strcmp(fun_name, #gtk_function))) \
506 return FNUM_ ## gtk_class ## _ ## gtk_function;
507
508 #include "Gtk/Gtk_map.def"
509
510 MORE_ERROR() << "function string class=" << wid_class
511 << ", function=" << fun_name << " could not be resolved";
512 return FNUM_INVALID;
513 }
514 //-----------------------------------------------------------------------------
515 void
clear()516 Quad_GTK::clear()
517 {
518 loop(w, open_windows.size()) close_window(open_windows[w].fd);
519 }
520 //-----------------------------------------------------------------------------
521 Value_P
close_window(int fd)522 Quad_GTK::close_window(int fd)
523 {
524 loop(w, open_windows.size())
525 {
526 window_entry & we = open_windows[w];
527 if (fd == we.fd)
528 {
529 we = open_windows.back();
530 open_windows.pop_back();
531 if (write_TL0(fd, 5))
532 {
533 CERR << "write(close Tag) failed in ⎕GTK::close_window()";
534 }
535 const int err = Quad_FIO::fun->close_handle(fd);
536 return IntScalar(err, LOC);
537 }
538 }
539
540 MORE_ERROR() << "Invalid ⎕GTK handle " << fd;
541 DOMAIN_ERROR;
542 }
543 //-----------------------------------------------------------------------------
544 int
write_TL0(int fd,int tag)545 Quad_GTK::write_TL0(int fd, int tag)
546 {
547 unsigned char TLV[8];
548 memset(TLV, 0, 8);
549 TLV[0] = tag >> 24;
550 TLV[1] = tag >> 16;
551 TLV[2] = tag >> 8;
552 TLV[3] = tag;
553
554 if (write(fd, TLV, 8) != 8)
555 {
556 CERR << "write(Tag = " << tag << ") failed in ⎕GTK::write_TL0()";
557 return -1;
558 }
559
560 return 0;
561 }
562 //-----------------------------------------------------------------------------
563 int
write_TLV(int fd,int tag,const UTF8_string & value)564 Quad_GTK::write_TLV(int fd, int tag, const UTF8_string & value)
565 {
566 const int TLV_len = 8 + value.size();
567 unsigned char TLV[TLV_len];
568 TLV[0] = tag >> 24 & 0xFF;
569 TLV[1] = tag >> 16 & 0xFF;
570 TLV[2] = tag >> 8 & 0xFF;
571 TLV[3] = tag & 0xFF;
572 TLV[4] = value.size() >> 24;
573 TLV[5] = value.size() >> 16;
574 TLV[6] = value.size() >> 8;
575 TLV[7] = value.size();
576 loop(s, value.size()) TLV[8 + s] = value[s];
577
578 if (write(fd, TLV, TLV_len) != TLV_len)
579 {
580 CERR << "write(Tag = " << tag << ") failed in ⎕GTK::write_TLV()";
581 return -1;
582 }
583
584 return 0;
585 }
586 //-----------------------------------------------------------------------------
587 int
open_window(const UCS_string & gui_filename,const UCS_string * css_filename)588 Quad_GTK::open_window(const UCS_string & gui_filename,
589 const UCS_string * css_filename)
590 {
591 // locate the Gtk_server. It should live in one of two places:
592 //
593 // 1, for an installed apl: in the same directory as apl, or
594 // 2. during development in subdir Gtk of the src directory
595 //
596 char path1[APL_PATH_MAX + 1 + 8];
597 char path2[APL_PATH_MAX + 1 + 8];
598 const char * bin_dir = LibPaths::get_APL_bin_path();
599 int slen = snprintf(path1, APL_PATH_MAX, "%s/Gtk/Gtk_server", bin_dir);
600 if (slen >= APL_PATH_MAX) path1[APL_PATH_MAX] = 0;
601 slen = snprintf(path2, APL_PATH_MAX, "%s/Gtk_server", bin_dir);
602 if (slen >= APL_PATH_MAX) path2[APL_PATH_MAX] = 0;
603
604 const char * path = 0;
605 if (!access(path1, X_OK)) path = path1;
606 else if (!access(path2, X_OK)) path = path2;
607 else
608 {
609 MORE_ERROR() << "No Gtk_server found in " << path1
610 << "\nor " << path2;
611 DOMAIN_ERROR;
612 }
613
614 const int fd = Quad_FIO::fun->do_FIO_57(path);
615
616 // write TLVs 1 and 3 or 1, 2, and 3 to Gtk_server...
617 //
618 UTF8_string gui_utf8(gui_filename);
619 slen = snprintf(path1 + 8, APL_PATH_MAX, "%s", gui_utf8.c_str());
620 if (slen >= APL_PATH_MAX) path1[APL_PATH_MAX] = 0;
621 memset(path1, 0, 8);
622 path1[3] = 1; // tag = 1: gui filename
623 path1[6] = gui_utf8.size() >> 8;
624 path1[7] = gui_utf8.size() & 0xFF;
625 slen = write(fd, path1, 8 + gui_utf8.size());
626 if (slen == -1)
627 {
628 Quad_FIO::fun->close_handle(fd);
629 MORE_ERROR() << "write(Tag 1) failed in ⎕GTK";
630 DOMAIN_ERROR;
631 }
632
633 if (css_filename)
634 {
635 UTF8_string css_utf8(*css_filename);
636 memset(path2, 0, 8);
637 slen = snprintf(path2 + 8, APL_PATH_MAX, "%s", css_utf8.c_str());
638 path2[3] = 2; // tag = 2: css filename
639 path2[6] = css_utf8.size() >> 8;
640 path2[7] = css_utf8.size() & 0xFF;
641 slen = write(fd, path2, 8 + css_utf8.size());
642 if (slen == -1)
643 {
644 Quad_FIO::fun->close_handle(fd);
645 MORE_ERROR() << "write(Tag 2) failed in ⎕GTK";
646 DOMAIN_ERROR;
647 }
648 }
649
650 if (write_TL0(fd, 3))
651 {
652 Quad_FIO::fun->close_handle(fd);
653 MORE_ERROR() << "write(Tag 3) failed in ⎕GTK";
654 DOMAIN_ERROR;
655 }
656
657 window_entry we = { fd };
658 open_windows.push_back(we);
659 return fd;
660 }
661 //-----------------------------------------------------------------------------
662
663 #else // not HAVE_GTK3
664
665 //-----------------------------------------------------------------------------
666 Token
eval_AB(Value_P A,Value_P B)667 Quad_GTK::eval_AB(Value_P A, Value_P B)
668 {
669 MORE_ERROR() << "libgtk+ version 3 was not found at ./configure time";
670 DOMAIN_ERROR;
671 }
672 //-----------------------------------------------------------------------------
673 Token
eval_B(Value_P B)674 Quad_GTK::eval_B(Value_P B)
675 {
676 MORE_ERROR() << "libgtk+ version 3 was not found at ./configure time";
677 DOMAIN_ERROR;
678 }
679 //-----------------------------------------------------------------------------
680 Token
eval_AXB(Value_P A,Value_P X,Value_P B)681 Quad_GTK::eval_AXB(Value_P A, Value_P X, Value_P B)
682 {
683 MORE_ERROR() << "libgtk+ version 3 was not found at ./configure time";
684 DOMAIN_ERROR;
685 }
686 //-----------------------------------------------------------------------------
687 Token
eval_XB(Value_P X,Value_P B)688 Quad_GTK::eval_XB(Value_P X, Value_P B)
689 {
690 MORE_ERROR() << "libgtk+ version 3 was not found at ./configure time";
691 DOMAIN_ERROR;
692 }
693 //-----------------------------------------------------------------------------
694 void
clear()695 Quad_GTK::clear()
696 {
697 }
698 //-----------------------------------------------------------------------------
699 #endif // HAVE_GTK3
700
701
702