Mercurial > octave-nkf
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 } |