| 51 | | ( inline_states => { |
| 52 | | %{ $Options{inline_states} }, |
| 53 | | |
| 54 | | _start => sub { |
| 55 | | $_[KERNEL]->select(\*STDIN, "-keyin"); |
| 56 | | |
| 57 | | # Turn blocking back on for STDIN. Some Curses |
| 58 | | # implementations don't deal well with non-blocking STDIN. |
| 59 | | my $flags = fcntl STDIN, F_GETFL, 0 or die $!; |
| 60 | | fcntl STDIN, F_SETFL, $flags & ~O_NONBLOCK or die $!; |
| 61 | | |
| 62 | | $ModalData{$RootObject} = {Objects => [$RootObject], Callbacks => [undef]}; |
| 63 | | set_read_timeout($RootObject); |
| 64 | | |
| 65 | | $_[HEAP] = $RootObject; |
| 66 | | |
| 67 | | $Options{inline_states}{_start}(@_) |
| 68 | | if defined $Options{inline_states}{_start}; |
| 69 | | }, |
| 70 | | |
| 71 | | -keyin => sub { |
| 72 | | $Curses::UI::rootobject = $_[HEAP]; |
| 73 | | my $modalObjects = $ModalData{$_[HEAP]}->{Objects}; |
| 74 | | my $modalCallbacks = $ModalData{$_[HEAP]}->{Callbacks}; |
| 75 | | |
| 76 | | unless ($#$modalObjects) { |
| 77 | | $RootObject->do_one_event; |
| 78 | | } |
| 79 | | else { |
| 80 | | $$modalObjects[TOP]->root->do_one_event($$modalObjects[TOP]); |
| 81 | | } |
| 82 | | Curses::curs_set($$modalObjects[ROOT]->{-cursor_mode}); |
| 83 | | |
| 84 | | # if (my $key = $_[HEAP]->get_key(0)) { |
| 85 | | # $RootObject->feedkey($key) unless $key eq '-1'; |
| 86 | | # $RootObject->do_one_event; |
| 87 | | # } |
| 88 | | |
| 89 | | |
| 90 | | # If this is a callback modal focus, and we lost modal focus, |
| 91 | | # do the callback. |
| 92 | | if (defined($$modalCallbacks[TOP]) && !$$modalObjects[TOP]->{-has_modal_focus} ) { |
| 93 | | $$modalObjects[TOP]->{-focus} = 0; |
| 94 | | pop @$modalObjects; |
| 95 | | my (@args) = @{pop @$modalCallbacks}; |
| 96 | | |
| 97 | | # Invoke the callback. |
| 98 | | my ($sub) = shift(@args); |
| 99 | | &{$sub} (@args); |
| 100 | | } |
| 101 | | }, |
| 102 | | |
| 103 | | -timer => sub { |
| 104 | | $Curses::UI::rootobject = $_[HEAP]; |
| 105 | | my $modalObjects = $ModalData{$_[HEAP]}->{Objects}; |
| 106 | | my $modalCallbacks = $ModalData{$_[HEAP]}->{Callbacks}; |
| 107 | | $$modalObjects[TOP]->do_timer; |
| 108 | | Curses::curs_set($$modalObjects[ROOT]->{-cursor_mode}); |
| 109 | | |
| 110 | | set_read_timeout($$modalObjects[TOP]); |
| 111 | | |
| 112 | | # If this is a callback modal focus, and we lost modal focus, |
| 113 | | # do the callback. |
| 114 | | if (defined($$modalCallbacks[TOP]) && !$$modalObjects[TOP]->{-has_modal_focus} ) { |
| 115 | | $$modalObjects[TOP]->{-focus} = 0; |
| 116 | | pop @$modalObjects; |
| 117 | | my ($cb) = pop @$modalCallbacks; |
| 118 | | |
| 119 | | # Invoke the callback. |
| 120 | | my ($sub) = shift(@$cb); |
| 121 | | my (@args) = @$cb; |
| 122 | | &{$sub} (@args); |
| 123 | | } |
| 124 | | }, |
| 125 | | |
| 126 | | shutdown => sub { |
| 127 | | $_[KERNEL]->select(\*STDIN); |
| 128 | | }, |
| 129 | | }, |
| 130 | | |
| 131 | | heap => $RootObject, |
| 132 | | ); |
| 133 | | |
| 134 | | $RootObject->{-no_output} = $Options{-no_output} || 0; |
| 135 | | |
| 136 | | return $RootObject; |
| | 61 | ( options => $options{options}, |
| | 62 | args => $options{args}, |
| | 63 | inline_states => $options{inline_states}, |
| | 64 | package_states => $options{package_states}, |
| | 65 | |
| | 66 | object_states => [ |
| | 67 | @{ $options{object_states} }, |
| | 68 | $self, [ qw( _start keyin timer shutdown ) ] |
| | 69 | ], |
| | 70 | |
| | 71 | # This is to maintain backward compatibility. |
| | 72 | heap => $self ); |
| | 73 | |
| | 74 | # Copy the no-output option |
| | 75 | $self->{-no_output} = $options{no_output} || 0; |
| | 76 | |
| | 77 | no warnings "redefine"; |
| | 78 | # Redefine the callbackmodalfocus to ensure that callbacks and objects make |
| | 79 | # it on to our own private stack. |
| | 80 | sub Curses::UI::Widget::callbackmodalfocus { |
| | 81 | my ($this, $cb) = @_; |
| | 82 | |
| | 83 | # "Fake" focus for this object. |
| | 84 | $this->{-has_modal_focus} = 1; |
| | 85 | $this->focus; |
| | 86 | $this->draw; |
| | 87 | |
| | 88 | push @modal_objects, $this; |
| | 89 | |
| | 90 | if (defined $cb) { |
| | 91 | # They need a callback, so register it. |
| | 92 | push @modal_callbacks, $cb; |
| | 93 | } else { |
| | 94 | # Push a null callback. |
| | 95 | push @modal_callbacks, [sub { }]; |
| | 96 | } |
| | 97 | |
| | 98 | # We assume our callers are going to return immediately back to the |
| | 99 | # main event loop, so we don't need a recursive call. |
| | 100 | return; |
| | 101 | } |
| | 102 | |
| | 103 | # Let modalfocus() be a reentrant into the POE Kernel. This is stackable, |
| | 104 | # so it should not impact other behaviors, and POE keeps chugging along |
| | 105 | # uneffected. This is a modal focus without a callback, this method does |
| | 106 | # not return until the modal widget get's cleared out. |
| | 107 | sub Curses::UI::Widget::modalfocus () { |
| | 108 | my ($this) = @_; |
| | 109 | |
| | 110 | # "Fake" focus for this object. |
| | 111 | $this->{-has_modal_focus} = 1; |
| | 112 | $this->focus; |
| | 113 | $this->draw; |
| | 114 | |
| | 115 | push @modal_objects, $this; |
| | 116 | push @modal_callbacks, undef; |
| | 117 | |
| | 118 | # This is reentrant into the POE::Kernel |
| | 119 | while ( $this->{-has_modal_focus} ) { |
| | 120 | $poe_kernel->loop_do_timeslice; |
| | 121 | } |
| | 122 | |
| | 123 | $this->{-focus} = 0; |
| | 124 | |
| | 125 | pop @modal_callbacks; |
| | 126 | pop @modal_objects; |
| | 127 | |
| | 128 | return $this; |
| | 129 | } |
| | 130 | |
| | 131 | return $self; |
| | 132 | } |
| | 133 | |
| | 134 | sub _start { |
| | 135 | my ($self, $kernel) = @_[ OBJECT, KERNEL ]; |
| | 136 | |
| | 137 | $kernel->select(\*STDIN, "-keyin"); |
| | 138 | |
| | 139 | # Turn blocking back on for STDIN. Some Curses |
| | 140 | # implementations don't deal well with non-blocking STDIN. |
| | 141 | my $flags = fcntl STDIN, F_GETFL, 0 or die $!; |
| | 142 | fcntl STDIN, F_SETFL, $flags & ~O_NONBLOCK or die $!; |
| | 143 | |
| | 144 | # This self reference is just so we can stack and peel onto the list of |
| | 145 | # modal objects, and get to ourselves when we reach the top. |
| | 146 | $self->{modal_objects} = [ $self ]; |
| | 147 | |
| | 148 | set_read_timeout($self); |
| | 149 | |
| | 150 | # Unmask... |
| | 151 | $self->{options}{inline_states}{_start}(@_) |
| | 152 | if (defined $self->{options}->{inline_states} && |
| | 153 | defined $self->{options}{inline_states}{_start}); |
| | 154 | } |
| | 155 | |
| | 156 | sub _clear_modal_callback { |
| | 157 | my ($self) = @_; |
| | 158 | |
| | 159 | my $top_object = pop @{ $self->{_modal_objects} }; |
| | 160 | |
| | 161 | # Reset focus |
| | 162 | $top_object->{-focus} = 0; |
| | 163 | |
| | 164 | # Dispatch callback. |
| | 165 | my @args = @{ pop @{ $self->{_modal_callbacks} } }; |
| | 166 | my ($sub) = shift @args; |
| | 167 | &{$sub}(@args); |
| | 168 | } |
| | 169 | |
| | 170 | sub keyin { |
| | 171 | my ($self, $kernel) = @_[ OBJECT, KERNEL ]; |
| | 172 | |
| | 173 | my $top_object = $self->{modal_objects}->[TOP]; |
| | 174 | # dispatch the event to the top-most modal object, or the root. |
| | 175 | $top_object->root->do_one_event($top_object); |
| | 176 | |
| | 177 | # Set the root cursor mode |
| | 178 | Curses::curs_set($self->{-cursor_mode}); |
| | 179 | |
| | 180 | _clear_modal_callback($self) unless $top_object->{-has_modal_focus}; |
| | 181 | } |
| | 182 | |
| | 183 | sub timer { |
| | 184 | my ($self) = @_; |
| | 185 | |
| | 186 | my $top_object = $self->{modal_objects}->[TOP]; |
| | 187 | # dispatch the event to the top-most modal object, or the root. |
| | 188 | $top_object->do_timer; |
| | 189 | |
| | 190 | # Set the root cursor mode. |
| | 191 | Curses::curs_set($self->{-cursor_mode}); |
| | 192 | |
| | 193 | set_read_timeout($top_object); |
| | 194 | |
| | 195 | _clear_modal_callback($self) unless $top_object->{-has_modal_focus}; |
| | 196 | } |
| | 197 | |
| | 198 | sub shutdown { |
| | 199 | my ($kernel) = $_[ KERNEL ]; |
| | 200 | |
| | 201 | # Unselect stdin |
| | 202 | $kernel->select(\*STDIN); |
| 178 | | } |
| 179 | | |
| 180 | | # Redefine the modalfocus loop because it sucks. |
| 181 | | { |
| 182 | | no warnings "redefine"; |
| 183 | | sub Curses::UI::Widget::modalfocus () { |
| 184 | | my $this = shift; |
| 185 | | |
| 186 | | # "Fake" focus for this object. |
| 187 | | $this->{-has_modal_focus} = 1; |
| 188 | | $this->focus; |
| 189 | | $this->draw; |
| 190 | | |
| 191 | | my $modalObjects = $ModalData{$Curses::UI::rootobject}->{Objects}; |
| 192 | | my $modalCallbacks = $ModalData{$Curses::UI::rootobject}->{Callbacks}; |
| 193 | | push @$modalObjects, $this; |
| 194 | | push @$modalCallbacks, undef; |
| 195 | | |
| 196 | | # This is reentrant into the POE::Kernel |
| 197 | | while ( $this->{-has_modal_focus} ) { |
| 198 | | $poe_kernel->loop_do_timeslice; |
| 199 | | } |
| 200 | | |
| 201 | | $this->{-focus} = 0; |
| 202 | | |
| 203 | | pop @$modalCallbacks; |
| 204 | | pop @$modalObjects; |
| 205 | | |
| 206 | | return $this; |
| 207 | | } |
| 208 | | |
| 209 | | |
| 210 | | sub Curses::UI::Widget::callbackmodalfocus { |
| 211 | | my $this = shift; |
| 212 | | my ($cb) = shift; |
| 213 | | |
| 214 | | # "Fake" focus for this object. |
| 215 | | $this->{-has_modal_focus} = 1; |
| 216 | | $this->focus; |
| 217 | | $this->draw; |
| 218 | | |
| 219 | | my $modalObjects = $ModalData{$Curses::UI::rootobject}->{Objects}; |
| 220 | | my $modalCallbacks = $ModalData{$Curses::UI::rootobject}->{Callbacks}; |
| 221 | | push @$modalObjects, $this; |
| 222 | | |
| 223 | | if (defined $cb) { |
| 224 | | # They need a callback, so register it. |
| 225 | | push @$modalCallbacks, $cb; |
| 226 | | } else { |
| 227 | | # Push a null callback. |
| 228 | | push @$modalCallbacks, [sub { }]; |
| 229 | | } |
| 230 | | |
| 231 | | # We assume our callers are going to return immediately back to the |
| 232 | | # main event loop, so we don't need a recursive call. |
| 233 | | return; |
| 234 | | } |