comparison src/graphics.cc @ 13924:3b654a0753b1

Implement waitfor, uiwait and uiresume. * liboctave/cmd-edit.h (command_editor::run_event_hooks): New static method. * liboctave/cmd-edit.cc (command_editor::run_event_hooks): Implement it. * src/graphics.h.in (listener_mode::PREDELETE): New enum value. * src/graphics.cc (<ctime>, "cutils.h"): New included headers. (base_properties::has_dynamic_properties): Look also into all_props. (gh_manager::do_execute_callback): Allow any type of function to be specified, not only function handles. (waitfor_results): New utility static variable. (compare_property_values, cleanup_waitfor_id, do_cleanup_waitfor_listener, cleanup_waitfor_postset_listener, cleanup_waitfor_predelete_listener, waitfor_listener, waitfor_del_listener): New utility static functions. (Fwaitfor): New function. * plot/uiwait.m: New function. * plot/uiresume.m: Likewise. * plot/modules.mk (plot_FCN_FILES): Add them to the list.
author Michael Goffioul <michael.goffioul@gmail.com>
date Wed, 23 Nov 2011 08:59:25 +0000
parents 2af665333b86
children ec435c4d8198
comparison
equal deleted inserted replaced
13923:7b83576b3b48 13924:3b654a0753b1
25 #endif 25 #endif
26 26
27 #include <cctype> 27 #include <cctype>
28 #include <cfloat> 28 #include <cfloat>
29 #include <cstdlib> 29 #include <cstdlib>
30 #include <ctime>
30 31
31 #include <algorithm> 32 #include <algorithm>
32 #include <list> 33 #include <list>
33 #include <map> 34 #include <map>
34 #include <set> 35 #include <set>
37 38
38 #include "file-ops.h" 39 #include "file-ops.h"
39 #include "file-stat.h" 40 #include "file-stat.h"
40 41
41 #include "cmd-edit.h" 42 #include "cmd-edit.h"
43 #include "cutils.h"
42 #include "defun.h" 44 #include "defun.h"
43 #include "display.h" 45 #include "display.h"
44 #include "error.h" 46 #include "error.h"
45 #include "graphics.h" 47 #include "graphics.h"
46 #include "input.h" 48 #include "input.h"
2482 bool 2484 bool
2483 base_properties::has_dynamic_property (const std::string& pname) 2485 base_properties::has_dynamic_property (const std::string& pname)
2484 { 2486 {
2485 const std::set<std::string>& dynprops = dynamic_property_names (); 2487 const std::set<std::string>& dynprops = dynamic_property_names ();
2486 2488
2487 return dynprops.find (pname) != dynprops.end (); 2489 if (dynprops.find (pname) != dynprops.end ())
2490 return true;
2491 else
2492 return all_props.find (pname) != all_props.end ();
2488 } 2493 }
2489 2494
2490 void 2495 void
2491 base_properties::set_dynamic (const caseless_str& pname, 2496 base_properties::set_dynamic (const caseless_str& pname,
2492 const octave_value& val) 2497 const octave_value& val)
7677 7682
7678 // Copy CB because "function_value" method is non-const. 7683 // Copy CB because "function_value" method is non-const.
7679 7684
7680 octave_value cb = cb_arg; 7685 octave_value cb = cb_arg;
7681 7686
7682 if (cb.is_function_handle ()) 7687 if (cb.is_function () || cb.is_function_handle ())
7683 fcn = cb.function_value (); 7688 fcn = cb.function_value ();
7684 else if (cb.is_string ()) 7689 else if (cb.is_string ())
7685 { 7690 {
7686 int status; 7691 int status;
7687 std::string s = cb.string_value (); 7692 std::string s = cb.string_value ();
7688 7693
7689 eval_string (s, false, status); 7694 eval_string (s, false, status);
7690 } 7695 }
7691 else if (cb.is_cell () && cb.length () > 0 7696 else if (cb.is_cell () && cb.length () > 0
7692 && (cb.rows () == 1 || cb.columns () == 1) 7697 && (cb.rows () == 1 || cb.columns () == 1)
7693 && cb.cell_value ()(0).is_function_handle ()) 7698 && (cb.cell_value ()(0).is_function ()
7699 || cb.cell_value ()(0).is_function_handle ()))
7694 { 7700 {
7695 Cell c = cb.cell_value (); 7701 Cell c = cb.cell_value ();
7696 7702
7697 fcn = c(0).function_value (); 7703 fcn = c(0).function_value ();
7698 if (! error_state) 7704 if (! error_state)
9431 else 9437 else
9432 error ("%s: invalid handle (= %g)", func.c_str(), handle); 9438 error ("%s: invalid handle (= %g)", func.c_str(), handle);
9433 9439
9434 return ret; 9440 return ret;
9435 } 9441 }
9442
9443 static bool
9444 compare_property_values (const octave_value& o1, const octave_value& o2)
9445 {
9446 octave_value_list args (2);
9447
9448 args(0) = o1;
9449 args(1) = o2;
9450
9451 octave_value_list result = feval ("isequal", args, 1);
9452
9453 if (! error_state && result.length () > 0)
9454 return result(0).bool_value ();
9455
9456 return false;
9457 }
9458
9459 static std::map<uint32_t, bool> waitfor_results;
9460
9461 static void
9462 cleanup_waitfor_id (uint32_t id)
9463 {
9464 waitfor_results.erase (id);
9465 }
9466
9467 static void
9468 do_cleanup_waitfor_listener (const octave_value& listener,
9469 listener_mode mode = POSTSET)
9470 {
9471 Cell c = listener.cell_value ();
9472
9473 if (c.numel () >= 4)
9474 {
9475 double h = c(2).double_value ();
9476
9477 if (! error_state)
9478 {
9479 caseless_str pname = c(3).string_value ();
9480
9481 if (! error_state)
9482 {
9483 gh_manager::auto_lock guard;
9484
9485 graphics_handle handle = gh_manager::lookup (h);
9486
9487 if (handle.ok ())
9488 {
9489 graphics_object go = gh_manager::get_object (handle);
9490
9491 if (go.get_properties ().has_property (pname))
9492 {
9493 go.get_properties ()
9494 .delete_listener (pname, listener, mode);
9495 if (mode == POSTSET)
9496 go.get_properties ()
9497 .delete_listener (pname, listener, PERSISTENT);
9498 }
9499 }
9500 }
9501 }
9502 }
9503 }
9504
9505 static void
9506 cleanup_waitfor_postset_listener(const octave_value& listener)
9507 { do_cleanup_waitfor_listener (listener, POSTSET); }
9508
9509 static void
9510 cleanup_waitfor_predelete_listener(const octave_value& listener)
9511 { do_cleanup_waitfor_listener (listener, PREDELETE); }
9512
9513 static octave_value_list
9514 waitfor_listener (const octave_value_list& args, int)
9515 {
9516 if (args.length () > 3)
9517 {
9518 uint32_t id = args(2).uint32_scalar_value ().value ();
9519
9520 if (! error_state)
9521 {
9522 if (args.length () > 5)
9523 {
9524 double h = args(0).double_value ();
9525
9526 if (! error_state)
9527 {
9528 caseless_str pname = args(4).string_value ();
9529
9530 if (! error_state)
9531 {
9532 gh_manager::auto_lock guard;
9533
9534 graphics_handle handle = gh_manager::lookup (h);
9535
9536 if (handle.ok ())
9537 {
9538 graphics_object go = gh_manager::get_object (handle);
9539 octave_value pvalue = go.get (pname);
9540
9541 if (compare_property_values (pvalue, args(5)))
9542 waitfor_results[id] = true;
9543 }
9544 }
9545 }
9546 }
9547 else
9548 waitfor_results[id] = true;
9549 }
9550 }
9551
9552 return octave_value_list ();
9553 }
9554
9555 static octave_value_list
9556 waitfor_del_listener (const octave_value_list& args, int)
9557 {
9558 if (args.length () > 2)
9559 {
9560 uint32_t id = args(2).uint32_scalar_value ().value ();
9561
9562 if (! error_state)
9563 waitfor_results[id] = true;
9564 }
9565
9566 return octave_value_list ();
9567 }
9568
9569 DEFUN (waitfor, args, ,
9570 "-*- texinfo -*-\n\
9571 @deftypefn {Built-in Function} {} waitfor (@var{h})\n\
9572 @deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop})\n\
9573 @deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop}, @var{value})\n\
9574 @deftypefnx {Built-in Function} {} waitfor (@dots{}, \"timeout\", @var{timeout})\n\
9575 Suspends the execution of the current program until a condition is\n\
9576 satisfied on the graphics handle @var{h}. While the program is suspended\n\
9577 graphics events are still being processed normally, allowing callbacks to\n\
9578 modify the state of graphics objects. This function is reentrant and can be\n\
9579 called from a callback, while another @code{waitfor} call is pending at\n\
9580 top-level.\n\
9581 \n\
9582 In the first form, program execution is suspended until the graphics object\n\
9583 @var{h} is destroyed. If the graphics handle is invalid, the function\n\
9584 returns immediately.\n\
9585 \n\
9586 In the second form, execution is suspended until the graphics object is\n\
9587 destroyed or the property named @var{prop} is modified. If the graphics\n\
9588 handle is invalid or the property does not exist, the function returns\n\
9589 immediately.\n\
9590 \n\
9591 In the third form, execution is suspended until the graphics object is\n\
9592 destroyed or the property named @var{prop} is set to @var{value}. The\n\
9593 function @code{isequal} is used to compare property values. If the graphics\n\
9594 handle is invalid, the property does not exist or the property is already\n\
9595 set to @var{value}, the function returns immediately.\n\
9596 \n\
9597 An optional timeout can be specified using the property @code{timeout}.\n\
9598 This timeout value is the number of seconds to wait for the condition to be\n\
9599 true. @var{timeout} must be at least 1. If a smaller value is specified, a\n\
9600 warning is issued and a value of 1 is used instead. If the timeout value is\n\
9601 not an integer, it is truncated towards 0.\n\
9602 \n\
9603 To define a condition on a property named @code{timeout}, use the string\n\
9604 @code{\\timeout} instead.\n\
9605 \n\
9606 In all cases, typing CTRL-C stops program execution immediately.\n\
9607 @seealso{isequal}\n\
9608 @end deftypefn")
9609 {
9610 if (args.length () > 0)
9611 {
9612 double h = args(0).double_value ();
9613
9614 if (! error_state)
9615 {
9616 caseless_str pname;
9617
9618 unwind_protect frame;
9619
9620 static uint32_t id_counter = 0;
9621 uint32_t id = 0;
9622
9623 int max_arg_index = 0;
9624 int timeout_index = -1;
9625
9626 int timeout = 0;
9627
9628 if (args.length () > 1)
9629 {
9630 pname = args(1).string_value ();
9631 if (! error_state
9632 && ! pname.empty ()
9633 && ! pname.compare ("timeout"))
9634 {
9635 if (pname.compare ("\\timeout"))
9636 pname = "timeout";
9637
9638 static octave_value wf_listener;
9639
9640 if (! wf_listener.is_defined ())
9641 wf_listener =
9642 octave_value (new octave_builtin (waitfor_listener,
9643 "waitfor_listener"));
9644
9645 max_arg_index++;
9646 if (args.length () > 2)
9647 {
9648 if (args(2).is_string ())
9649 {
9650 caseless_str s = args(2).string_value ();
9651
9652 if (! error_state)
9653 {
9654 if (s.compare ("timeout"))
9655 timeout_index = 2;
9656 else
9657 max_arg_index++;
9658 }
9659 }
9660 else
9661 max_arg_index++;
9662 }
9663
9664 Cell listener (1, max_arg_index >= 2 ? 5 : 4);
9665
9666 id = id_counter++;
9667 frame.add_fcn (cleanup_waitfor_id, id);
9668 waitfor_results[id] = false;
9669
9670 listener(0) = wf_listener;
9671 listener(1) = octave_uint32 (id);
9672 listener(2) = h;
9673 listener(3) = pname;
9674
9675 if (max_arg_index >= 2)
9676 listener(4) = args(2);
9677
9678 octave_value ov_listener (listener);
9679
9680 gh_manager::auto_lock guard;
9681
9682 graphics_handle handle = gh_manager::lookup (h);
9683
9684 if (handle.ok ())
9685 {
9686 graphics_object go = gh_manager::get_object (handle);
9687
9688 if (max_arg_index >= 2
9689 && compare_property_values (go.get (pname),
9690 args(2)))
9691 waitfor_results[id] = true;
9692 else
9693 {
9694
9695 frame.add_fcn (cleanup_waitfor_postset_listener,
9696 ov_listener);
9697 go.add_property_listener (pname, ov_listener,
9698 POSTSET);
9699 go.add_property_listener (pname, ov_listener,
9700 PERSISTENT);
9701
9702 if (go.get_properties ()
9703 .has_dynamic_property (pname))
9704 {
9705 static octave_value wf_del_listener;
9706
9707 if (! wf_del_listener.is_defined ())
9708 wf_del_listener =
9709 octave_value (new octave_builtin
9710 (waitfor_del_listener,
9711 "waitfor_del_listener"));
9712
9713 Cell del_listener (1, 4);
9714
9715 del_listener(0) = wf_del_listener;
9716 del_listener(1) = octave_uint32 (id);
9717 del_listener(2) = h;
9718 del_listener(3) = pname;
9719
9720 octave_value ov_del_listener (del_listener);
9721
9722 frame.add_fcn (cleanup_waitfor_predelete_listener,
9723 ov_del_listener);
9724 go.add_property_listener (pname, ov_del_listener,
9725 PREDELETE);
9726 }
9727 }
9728 }
9729 }
9730 else if (error_state || pname.empty ())
9731 error ("waitfor: invalid property name, expected a non-empty string value");
9732 }
9733
9734 if (! error_state
9735 && timeout_index < 0
9736 && args.length () > (max_arg_index + 1))
9737 {
9738 caseless_str s = args(max_arg_index + 1).string_value ();
9739
9740 if (! error_state)
9741 {
9742 if (s.compare ("timeout"))
9743 timeout_index = max_arg_index + 1;
9744 else
9745 error ("waitfor: invalid parameter `%s'", s.c_str ());
9746 }
9747 else
9748 error ("waitfor: invalid parameter, expected `timeout'");
9749 }
9750
9751 if (! error_state && timeout_index >= 0)
9752 {
9753 if (args.length () > (timeout_index + 1))
9754 {
9755 timeout = static_cast<int>
9756 (args(timeout_index + 1).scalar_value ());
9757
9758 if (! error_state)
9759 {
9760 if (timeout < 1)
9761 {
9762 warning ("waitfor: the timeout value must be >= 1, using 1 instead");
9763 timeout = 1;
9764 }
9765 }
9766 else
9767 error ("waitfor: invalid timeout value, expected a value >= 1");
9768 }
9769 else
9770 error ("waitfor: missing timeout value");
9771 }
9772
9773 // FIXME: There is still a "hole" in the following loop. The code
9774 // assumes that an object handle is unique, which is a fair
9775 // assumptions, except for figures. If a figure is destroyed
9776 // then recreated with the same figure ID, within the same
9777 // run of event hooks, then the figure destruction won't be
9778 // caught and the loop will not stop. This is an unlikely
9779 // possibility in practice, though.
9780 //
9781 // Using deletefcn callback is also unreliable as it could be
9782 // modified during a callback execution and the waitfor loop
9783 // would not stop.
9784 //
9785 // The only "good" implementation would require object
9786 // listeners, similar to property listeners.
9787
9788 time_t start = 0;
9789
9790 if (timeout > 0)
9791 start = time (0);
9792
9793 while (! error_state)
9794 {
9795 if (true)
9796 {
9797 gh_manager::auto_lock guard;
9798
9799 graphics_handle handle = gh_manager::lookup (h);
9800
9801 if (handle.ok ())
9802 {
9803 if (! pname.empty () && waitfor_results[id])
9804 break;
9805 }
9806 else
9807 break;
9808 }
9809
9810 #if defined (WIN32) && ! defined (__CYGWIN__)
9811 Sleep (100);
9812 #else
9813 octave_usleep (100000);
9814 #endif
9815
9816 OCTAVE_QUIT;
9817
9818 command_editor::run_event_hooks ();
9819
9820 if (timeout > 0)
9821 {
9822 if (start + timeout < time (0))
9823 break;
9824 }
9825 }
9826 }
9827 else
9828 error ("waitfor: invalid handle value.");
9829 }
9830 else
9831 print_usage ();
9832
9833 return octave_value ();
9834 }