Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-mt.1391.mcz
==================== Summary ====================
Name: Kernel-mt.1391
Author: mt
Time: 25 April 2021, 8:25:06.787934 am
UUID: 9ab61f3b-e0c8-c545-82fd-49bf4a28fbf4
Ancestors: Kernel-nice.1390
Changes keystroke mappings for better cross-platform compatibility. See http://forum.world.st/Please-try-it-out-Fixing-the-input-mapping-for-keystr…
=============== Diff against Kernel-nice.1390 ===============
Item was changed:
Object subclass: #EventSensor
instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore lastEventTime'
+ classVariableNames: 'ButtonDecodeTable EventPollPeriod EventTicklerProcess InterruptWatcherProcess KeyDecodePreferences KeyDecodeTable'
- classVariableNames: 'ButtonDecodeTable EventPollPeriod EventTicklerProcess InterruptWatcherProcess KeyDecodeTable'
poolDictionaries: 'EventSensorConstants'
category: 'Kernel-Processes'!
!EventSensor commentStamp: 'mt 12/13/2019 14:38' prior: 0!
An EventSensor is an interface to the user input devices.
There is at least one instance of EventSensor named Sensor in the system.
EventSensor is a replacement for the earlier InputSensor implementation based on a set of (optional) event primitives. An EventSensor updates its state when events are received so that all state based users of Sensor (e.g., Sensor keyboard, Sensor leftShiftDown, Sensor mouseButtons) will work exactly as before, by moving the current VM mechanisms into EventSensor itself. An optional input semaphore is part of the new design.
For platforms that support true asynchronous event notification, the semaphore will be signaled to indicate pending events.
On platforms that do not support asynchronous notifications about events, the UI will have to poll EventSensor periodically to read events from the VM.
Instance variables:
mouseButtons <Integer> - mouse button state as replacement for primMouseButtons
mousePosition <Point> - mouse position as replacement for primMousePt
keyboardBuffer <SharedQueue> - keyboard input buffer
interruptKey <Integer> - currently defined interrupt key
interruptSemaphore <Semaphore> - the semaphore signaled when the interruptKey is detected
eventQueue <SharedQueue> - an optional event queue for event driven applications
inputSemaphore <Semaphore>- the semaphore signaled by the VM if asynchronous event notification is supported
lastEventPoll <Integer> - the last millisecondClockValue at which we called fetchMoreEvents
hasInputSemaphore <Boolean> - true if my inputSemaphore has actually been signaled at least once.
Class variables:
ButtonDecodeTable <ByteArray> - maps mouse buttons as reported by the VM to ones reported in the events.
KeyDecodeTable <Dictionary<SmallInteger->SmallInteger>> - maps some keys and their modifiers to other keys (used for instance to map Ctrl-X to Alt-X)
InterruptSemaphore <Semaphore> - signalled by the the VM and/or the event loop upon receiving an interrupt keystroke.
InterruptWatcherProcess <Process> - waits on the InterruptSemaphore and then responds as appropriate.
EventPollPeriod <Integer> - the number of milliseconds to wait between polling for more events in the userInterruptHandler.
EventTicklerProcess <Process> - the process that makes sure that events are polled for often enough (at least every EventPollPeriod milliseconds).
Event format:
The current event format is very simple. Each event is recorded into an 8 element array. All events must provide some SmallInteger ID (the first field in the event buffer) and a time stamp (the second field in the event buffer), so that the difference between the time stamp of an event and the current time can be reported.
Currently, the following events are defined:
Null event
=============
The Null event is returned when the ST side asks for more events but no more events are available.
Structure:
[1] - event type 0
[2-8] - unused
Mouse event structure
==========================
Mouse events are generated when mouse input is detected.
[1] - event type 1
[2] - time stamp
[3] - mouse x position
[4] - mouse y position
[5] - button state; bitfield with the following entries:
1 - 2r001 yellow (e.g., right) button
2 - 2r010 blue (e.g., middle) button
4 - 2r100 red (e.g., left) button
[all other bits are currently undefined]
[6] - modifier keys; bitfield with the following entries:
1 - shift key
2 - ctrl key
4 - (Mac specific) option key
8 - Cmd/Alt key
[all other bits are currently undefined]
[7] - reserved.
[8] - host window id.
Keyboard events
====================
Keyboard events are generated when keyboard input is detected.
[1] - event type 2
[2] - time stamp
[3] - character code (Ascii)
For now the character code is in Mac Roman encoding. See #macToSqueak.
For key press/release (see [4]), character codes are normalized.
[4] - press state; integer with the following meaning
0 - character (aka. key stroke or key still pressed)
1 - key press (aka. key down)
2 - key release (aka. key up)
[5] - modifier keys (same as in mouse events)
For key press/release (see [4]), modifier keys are still accessible.
[6] - character code (Unicode UTF32)
Manual decoding via KeyboardInputInterpreter possible.
For key press/release (see [4]), character codes are normalized.
[7] - reserved.
[8] - host window id.
Mouse-wheel event structure
==========================
Mouse-wheel events are generated when mouse-wheel input is detected.
[1] - event type 7
[2] - time stamp
[3] - horizontal scroll delta
[4] - vertical scroll delta
[5] - button state (same as in mouse events)
[6] - modifier keys (same as in mouse events)
[7] - reserved.
[8] - host window id.
!
Item was removed:
- ----- Method: EventSensor class>>defaultCrossPlatformKeys (in category 'class initialization') -----
- defaultCrossPlatformKeys
- "Answer a list of key letters that are used for common editing operations
- on different platforms."
- ^{ $c . $x . $v . $a . $s . $f . $g . $z }
- !
Item was removed:
- ----- Method: EventSensor class>>duplicateAllControlAndAltKeysChanged (in category 'preference change notification') -----
- duplicateAllControlAndAltKeysChanged
- "The Preference for duplicateAllControlAndAltKeys has changed; reset the other two."
- "At some point the various exclusive CtrlAlt-key prefs should become a radio button set, then these methods wouldn't be needed."
- (Preferences
- valueOfFlag: #swapControlAndAltKeys
- ifAbsent: [false]) ifTrue: [
- self inform: 'Resetting swapControlAndAltKeys preference'.
- (Preferences preferenceAt: #swapControlAndAltKeys) rawValue: false.
- ].
- (Preferences
- valueOfFlag: #duplicateControlAndAltKeys
- ifAbsent: [false]) ifTrue: [
- self inform: 'Resetting duplicateControlAndAltKeys preference'.
- (Preferences preferenceAt: #duplicateControlAndAltKeys) rawValue: false.
- ].
- self installKeyDecodeTable.
- !
Item was changed:
----- Method: EventSensor class>>duplicateControlAndAltKeys: (in category 'public') -----
duplicateControlAndAltKeys: aBoolean
"EventSensor duplicateControlAndAltKeys: true"
+ self flag: #deprecated.
+ self
+ mapControlKeysToCommandKeys: aBoolean;
+ mapAltKeysToOptionKeys: false.!
- Preferences setPreference: #duplicateControlAndAltKeys toValue: aBoolean.
- self installKeyDecodeTable
- !
Item was removed:
- ----- Method: EventSensor class>>duplicateControlAndAltKeysChanged (in category 'preference change notification') -----
- duplicateControlAndAltKeysChanged
- "The Preference for duplicateControlAndAltKeys has changed; reset the other two."
- (Preferences
- valueOfFlag: #swapControlAndAltKeys
- ifAbsent: [false]) ifTrue: [
- self inform: 'Resetting swapControlAndAltKeys preference'.
- (Preferences preferenceAt: #swapControlAndAltKeys) rawValue: false.
- ].
- (Preferences
- valueOfFlag: #duplicateAllControlAndAltKeys
- ifAbsent: [false]) ifTrue: [
- self inform: 'Resetting duplicateAllControlAndAltKeys preference'.
- (Preferences preferenceAt: #duplicateAllControlAndAltKeys) rawValue: false.
- ].
- self installKeyDecodeTable.
- !
Item was added:
+ ----- Method: EventSensor class>>fixControlCharacters (in category 'key decode table') -----
+ fixControlCharacters
+ "Assure that all keyboard events that arrive with the CONTROL modifier actually have control characters set."
+
+ 64 "$@" to: 95 "$_"do: [:upper |
+ KeyDecodeTable
+ at: { upper . 2 bitOr: 1 "ctrl+shift" }
+ put: { upper bitAnd: 16r9F . 2 bitOr: 1 "ctrl+shift" }].
+ 96 "$`" to: 126 "$~" do: [:lower | "Ignore 127"
+ KeyDecodeTable
+ at: { lower . 2 "ctrl" }
+ put: { lower bitAnd: 16r9F . 2 "ctrl" }].
+
+ self flag: #linuxOnly. "mt: For Linux VMs as of version 201911282316, no control characters will be sent from the VM. Avoid check for #platformName because the extra mapping will not affect others anyway."
+ self flag: #windowsOnly. "mt: CTRL+m would not arrive as CTRL+CR, which is rather inconsistent."!
Item was changed:
----- Method: EventSensor class>>initialize (in category 'class initialization') -----
initialize
+
-
Smalltalk addToStartUpList: self before: ProcessorScheduler.
Smalltalk addToShutDownList: self.
+ KeyDecodePreferences := Dictionary new.
+
self installKeyDecodeTable.
+ self installMouseDecodeTable.!
- self installMouseDecodeTable.
- self install.
-
- !
Item was removed:
- ----- Method: EventSensor class>>installDuplicateKeyEntryFor: (in category 'key decode table') -----
- installDuplicateKeyEntryFor: aPrintableCharacter
- "Updates the key-decode table, which maps between pairs of {character code . modifier code}.
- See the class comment for more information.
- The purpose of this change is to let ctrl+key act like cmd+key (Mac) or alt+key (linux/windows).
- It is especially usefull on windows VM where default feel is to use ctrl as shortcut (ctrl+C = copy, etc...).
- Note that the bitmask 16r9F removes the high bits, which subtracts 64 from the key code for (upper) $A to $Z and 96 for (lower) $a to $z. The VM sends non-printable control characters for [ctrl]+[A-Za-Z] in ASCII < 32, but the given character is expected to be ASCII >= 32 and thus printable. So we have to convert control characters to printable characters in this mapping table."
-
- | upper lower |
- upper := aPrintableCharacter asUppercase asInteger.
- lower := aPrintableCharacter asLowercase asInteger.
-
- KeyDecodeTable at: { lower bitAnd: 16r9F . 2 "ctrl" } put: { lower . 8 "cmd/alt" }.
- KeyDecodeTable at: { upper bitAnd: 16r9F . 2 bitOr: 1 "ctrl + shift" } put: { upper . 8 bitOr: 1 "cmd/alt + shift" }.!
Item was changed:
----- Method: EventSensor class>>installKeyDecodeTable (in category 'class initialization') -----
installKeyDecodeTable
"Create a decode table that swaps or duplicates some keys if the respective preference is set."
KeyDecodeTable := Dictionary new.
+ self mapControlKeysToCommandKeys
+ ifTrue: [self installMappingToCommandKeys]
+ ifFalse: [self mapControlCharactersToPrintableCharacters
+ ifTrue: [self installMappingToPrintableCharacters]
+ ifFalse: [self fixControlCharacters "mt: Due to platform-specific VM behavior."]].
- Preferences swapControlAndAltKeys
- ifTrue: [ (Character allByteCharacters select: [:ea | ea isAlphaNumeric]) do:
- [ :c | self installSwappedKeyEntryFor: c ] ].
- Preferences duplicateAllControlAndAltKeys
- ifTrue: [ (Character allByteCharacters select: [:ea | ea isAlphaNumeric]) do:
- [ :c | self installDuplicateKeyEntryFor: c ] ].
+ self mapAltKeysToOptionKeys
+ ifTrue: [self installMappingToOptionKeys].!
- self flag: #toDeprecate. "mt: This mapping should be deprecated in the future."
- Preferences duplicateControlAndAltKeys
- ifTrue: [ self defaultCrossPlatformKeys do:
- [ :c | self installDuplicateKeyEntryFor: c ] ].
- !
Item was added:
+ ----- Method: EventSensor class>>installMappingToCommandKeys (in category 'key decode table') -----
+ installMappingToCommandKeys
+ "Maps all keyboard events that arrive with the CONTROL modifier to also have the COMMAND modifier set. This mapping also considers the preference #mapControlCharactersToPrintableCharacters."
+
+ | controlMask |
+ 0 "NUL" to: 27 "ESC" do: [:control |
+ controlMask := self mapControlCharactersToPrintableCharacters
+ ifTrue: [16r60] ifFalse: [0].
+ KeyDecodeTable
+ at: { control . 2r0010 "ctrl" }
+ put: { control bitOr: controlMask . 2r1010 "cmd+ctrl" }.
+ controlMask := self mapControlCharactersToPrintableCharacters
+ ifTrue: [16r40] ifFalse: [0].
+ KeyDecodeTable
+ at: { control . 2r0011 "ctrl+shift" }
+ put: { control bitOr: controlMask . 2r1011 "cmd+ctrl+shift" }].
+
+ 28 "arrow keys" to: 126 "$~" do: [:arrowAndPrintable |
+ KeyDecodeTable
+ at: { arrowAndPrintable . 2r0010 "ctrl" }
+ put: { arrowAndPrintable . 2r1010 "cmd+ctrl" }.
+ KeyDecodeTable
+ at: { arrowAndPrintable . 2r0011 "ctrl + shift" }
+ put: { arrowAndPrintable . 2r1011 "cmd + shift" }].!
Item was added:
+ ----- Method: EventSensor class>>installMappingToOptionKeys (in category 'key decode table') -----
+ installMappingToOptionKeys
+ "Maps all keyboard events that arrive with the COMMAND modifier to also have the OPTION modifier set. See preference #mapAltKeysToOptionKeys."
+
+ self flag: #windowsOnly. "mt: In Windows VMs version 202104182333, pressing the (physical) ALT key arrives as CMD modifier in the image. Should this ever change, this mapping MUST be adapted, too."
+ self flag: #linuxOnly. "mt: See #windowsOnly."
+ self flag: #macOSOnly. "mt: This mapping should be ignored to not overshadow events that have the actual OPTION modifier set."
+
+ CharacterSet ascii do: [:character |
+ KeyDecodeTable
+ at: { character asInteger . 2r1000 "cmd" }
+ put: { character asInteger . 2r1100 "cmd+opt" }.
+ KeyDecodeTable
+ at: { character asInteger . 2r1001 "cmd + shift" }
+ put: { character asInteger . 2r1101 "cmd + opt + shift" }].!
Item was added:
+ ----- Method: EventSensor class>>installMappingToPrintableCharacters (in category 'key decode table') -----
+ installMappingToPrintableCharacters
+ "Only applies when the CONTROL modifier is present!! Control characters that can directly be triggered -- such as CR, ENTER, BS, DEL, POS1, END -- will not be mapped."
+
+ self flag: #windowsOnly. "mt: The CONTROL modifier might directly change the control character. Examples: CTRL+CR(13) arrives as CTRL+LF(10), CTRL+BS(8) arrives as CTRL+DEL(127). If you have no other means to input LF(10) or DEL(127), you might have to disable this mapping to printable characters."
+
+ 0 "NUL" to: 27 "ESC" do: [:control |
+ KeyDecodeTable
+ at: { control . 2r0010 "ctrl" }
+ put: { control bitOr: 16r60 "+96" . 2 "ctrl" }.
+ KeyDecodeTable
+ at: { control . 2r0011 "ctrl+shift" }
+ put: { control bitOr: 16r40 "+64" . 2r0011 "ctrl+shift" }].!
Item was removed:
- ----- Method: EventSensor class>>installSwappedKeyEntryFor: (in category 'key decode table') -----
- installSwappedKeyEntryFor: aPrintableCharacter
- "Updates the key-decode table, which maps between pairs of {character code . modifier code}. See the class comment for more information.
- Note that the bitmask 16r9F removes the high bits, which subtracts 64 from the key code for (upper) $A to $Z and 96 for (lower) $a to $z. The VM sends non-printable control characters for [ctrl]+[A-Za-Z] in ASCII < 32, but the given character is expected to be ASCII >= 32 and thus printable. So we have to convert printable characters to control characters in this mapping table."
-
- | upper lower |
- upper := aPrintableCharacter asUppercase asInteger.
- lower := aPrintableCharacter asLowercase asInteger.
-
- KeyDecodeTable at: { lower bitAnd: 16r9F . 2 "ctrl" } put: { lower . 8 "cmd/alt" }.
- KeyDecodeTable at: { lower . 8 "cmd/alt" } put: { lower bitAnd: 16r9F . 2 "ctrl" }.
- KeyDecodeTable at: { upper bitAnd: 16r9F . 2 bitOr: 1 "ctrl+shift" } put: { upper . 8 bitOr: 1 "cmd/alt+shift" }.
- KeyDecodeTable at: { upper . 8 bitOr: 1 "cmd/alt+shift" } put: { upper bitAnd: 16r9F . 2 bitOr: 1 "ctrl+shift" }.!
Item was added:
+ ----- Method: EventSensor class>>mapAltKeysToOptionKeys (in category 'preferences') -----
+ mapAltKeysToOptionKeys
+ <preference: 'Map ALT keys to OPTION keys'
+ categoryList: #(keyboard events input)
+ description: 'On platforms other than macOS, keystrokes involving the (physical) ALT modifier typically arrive as COMMAND modifier. If you now also map CONTROL keys to COMMAND keys, you disable potential input. If you enable this preference, you preserve the possibility of reacting to two different modifiers -- CMD and OPT -- in your code, on all platforms. (This preference replaces older duplicate/swap preferences.)'
+ type: #Boolean>
+
+ ^ KeyDecodePreferences
+ at: #mapAltKeysToOptionKeys
+ ifAbsent: [false]!
Item was added:
+ ----- Method: EventSensor class>>mapAltKeysToOptionKeys: (in category 'preferences') -----
+ mapAltKeysToOptionKeys: aBooleanOrNil
+
+ aBooleanOrNil = self mapAltKeysToOptionKeys
+ ifTrue: [^ self].
+
+ aBooleanOrNil
+ ifNil: [
+ KeyDecodePreferences
+ removeKey: #mapAltKeysToOptionKeys]
+ ifNotNil: [
+ KeyDecodePreferences
+ at: #mapAltKeysToOptionKeys
+ put: aBooleanOrNil].
+
+ self installKeyDecodeTable.!
Item was added:
+ ----- Method: EventSensor class>>mapControlCharactersToPrintableCharacters (in category 'preferences') -----
+ mapControlCharactersToPrintableCharacters
+ <preference: 'Map ASCII control characters to printable characters'
+ categoryList: #(keyboard events input)
+ description: 'Keystrokes involving the (physical) CONTROL modifier typically have characters codes from 00 to 31 when pressing the physical keys labeled A to Z (or actually ASCII 64 to 95). This means that, for example in CTRL+C, client code cannot check for "anEvent controlKeyPressed and: [anEvent keyCharacter = $C]" but has to resort to "anEvent controlKeyPressed and: [keyCharacter = 3]." If you enable this preference all control characters will be mapped to printable characters.'
+ type: #Boolean>
+
+ ^ KeyDecodePreferences
+ at: #mapControlCharactersToPrintableCharacters
+ ifAbsent: [true]!
Item was added:
+ ----- Method: EventSensor class>>mapControlCharactersToPrintableCharacters: (in category 'preferences') -----
+ mapControlCharactersToPrintableCharacters: aBooleanOrNil
+
+ aBooleanOrNil
+ ifNil: [
+ KeyDecodePreferences
+ removeKey: #mapControlCharactersToPrintableCharacters]
+ ifNotNil: [
+ KeyDecodePreferences
+ at: #mapControlCharactersToPrintableCharacters
+ put: aBooleanOrNil].
+
+ self installKeyDecodeTable.!
Item was added:
+ ----- Method: EventSensor class>>mapControlKeysToCommandKeys (in category 'preferences') -----
+ mapControlKeysToCommandKeys
+ <preference: 'Map CONTROL keys to COMMAND keys'
+ categoryList: #(keyboard events input)
+ description: 'On platforms other than macOS, users use the CONTROL modifier for many common shortcuts around text editing such as CTRL+C and CTRL+V for copy and paste. For the sake of cross-platform compatibility, however, many tools in this system will check for CMD+C and CMD+V instead. Note that on Linux and Windows platforms, keystrokes involving the (physical) ALT modifier typically arrive as COMMAND modifier. Thus, enable this preference if you still want to use CONTROL key instead of the ALT key for such shortcuts. (This preference replaces older duplicate/swap preferences.)'
+ type: #Boolean>
+
+ ^ KeyDecodePreferences
+ at: #mapControlKeysToCommandKeys
+ ifAbsent: [true]!
Item was added:
+ ----- Method: EventSensor class>>mapControlKeysToCommandKeys: (in category 'preferences') -----
+ mapControlKeysToCommandKeys: aBooleanOrNil
+
+ aBooleanOrNil
+ ifNil: [
+ KeyDecodePreferences
+ removeKey: #mapControlKeysToCommandKeys]
+ ifNotNil: [
+ KeyDecodePreferences
+ at: #mapControlKeysToCommandKeys
+ put: aBooleanOrNil].
+
+ self installKeyDecodeTable.!
Item was changed:
----- Method: EventSensor class>>startUp (in category 'system startup') -----
startUp
+ Smalltalk platformName = 'Mac OS'
+ ifTrue: [
+ self mapAltKeysToOptionKeys: false.
+ self mapControlKeysToCommandKeys: false]
+ ifFalse: [
+ self mapAltKeysToOptionKeys: true.
+ self mapControlKeysToCommandKeys: true].
+
+ self default startUp.!
- self installMouseDecodeTable.
- self installKeyDecodeTable.
- self default startUp!
Item was changed:
----- Method: EventSensor class>>swapControlAndAltKeys: (in category 'public') -----
swapControlAndAltKeys: aBoolean
- "EventSensor swapControlAndAltKeys: true"
+ self deprecated: 'You cannnot swap CONTROL and ALT modifiers anymore. Please use other key-mapping preferences to fit your needs.'.!
- Preferences setPreference: #swapControlAndAltKeys toValue: aBoolean.
- self installKeyDecodeTable!
Item was removed:
- ----- Method: EventSensor class>>swapControlAndAltKeysChanged (in category 'preference change notification') -----
- swapControlAndAltKeysChanged
- "The Preference for swapControlAndAltKeys has changed; reset the other two."
- (Preferences
- valueOfFlag: #duplicateControlAndAltKeys
- ifAbsent: [false]) ifTrue: [
- self inform: 'Resetting duplicateControlAndAltKeys preference'.
- (Preferences preferenceAt: #duplicateControlAndAltKeys) rawValue: false.
- ].
- (Preferences
- valueOfFlag: #duplicateAllControlAndAltKeys
- ifAbsent: [false]) ifTrue: [
- self inform: 'Resetting duplicateAllControlAndAltKeys preference'.
- (Preferences preferenceAt: #duplicateAllControlAndAltKeys) rawValue: false.
- ].
- self installKeyDecodeTable.
- !
Item was changed:
----- Method: EventSensor>>anyModifierKeyPressed (in category 'modifier keys') -----
anyModifierKeyPressed
+ "ignore, however, the shift keys 'cause that's not REALLY a modifier key"
- "ignore, however, the shift keys 'cause that's not REALLY a command key"
+ ^ self peekButtons anyMask: (2r1110 "cmd | opt | ctrl" bitShift: MouseEvent numButtons)!
- ^ self peekButtons anyMask: 16r70 "cmd | opt | ctrl"!
Item was changed:
----- Method: EventSensor>>commandKeyPressed (in category 'modifier keys') -----
commandKeyPressed
"Answer whether the command key on the keyboard is being held down."
+ ^ self peekButtons anyMask: (2r1000 "cmd" bitShift: MouseEvent numButtons)!
- ^ self peekButtons anyMask: (1 bitShift: MouseEvent numButtons + 3)!
Item was changed:
----- Method: EventSensor>>controlKeyPressed (in category 'modifier keys') -----
controlKeyPressed
"Answer whether the control key on the keyboard is being held down."
+ ^ self peekButtons anyMask: (2r0010 "ctrl" bitShift: MouseEvent numButtons)!
- ^self peekButtons anyMask: (1 bitShift: MouseEvent numButtons + 1)!
Item was added:
+ ----- Method: EventSensor>>optionKeyPressed (in category 'modifier keys') -----
+ optionKeyPressed
+ "Answer whether the option key on the Macintosh keyboard is being held down. Macintosh specific. Clients are discouraged from calling this directly, since it circumvents bert's attempt to eradicate option-key checks"
+
+ ^ self peekButtons anyMask: (2r0100 "opt" bitShift: MouseEvent numButtons)!
Item was changed:
----- Method: EventSensor>>processEvent: (in category 'private-I/O') -----
processEvent: evt
"Process a single event. This method is run at high priority."
| type buttons window |
type := evt at: 1.
lastEventTime := evt at: 2.
"Only process main window events, forward others to host window proxies"
window := evt at: 8.
(window isNil or: [window isZero]) ifTrue:
[window := 1.
evt at: 8 put: window].
window = 1 ifFalse: [
^Smalltalk at: #HostWindowProxy ifPresent: [:w | w processEvent: evt]].
"Tackle mouse events and mouse wheel events first"
(type = EventTypeMouse or: [type = EventTypeMouseWheel])
ifTrue: [buttons := (ButtonDecodeTable at: (evt at: 5) + 1).
evt at: 5 put: (Smalltalk platformName = 'Mac OS'
ifTrue: [ buttons ]
ifFalse: [ self mapButtons: buttons modifiers: (evt at: 6) ]).
self queueEvent: evt.
type = EventTypeMouseWheel
ifTrue: [^ self processMouseWheelEvent: evt].
type = EventTypeMouse
ifTrue: [^ self processMouseEvent: evt]].
"Store the event in the queue if there's any"
type = EventTypeKeyboard
ifTrue: [ "Check if the event is a user interrupt"
((evt at: 4) = EventKeyChar
and: [((evt at: 3)
bitOr: (((evt at: 5)
bitAnd: 8)
bitShift: 8))
= interruptKey])
ifTrue: ["interrupt key is meta - not reported as event"
^ interruptSemaphore signal].
+ "Decode keys for characters (e.g., map ctrl -> cmd)."
- "Decode keys for characters (i.e., duplicate or swap, ctrl <-> alt/cmd)."
(evt at: 4) = EventKeyChar
ifTrue: [ | unicode ascii |
"Copy lookup key first in case of key swap."
unicode := {evt at: 6. evt at: 5}.
ascii := {evt at: 3. evt at: 5}.
KeyDecodeTable "Unicode character first"
at: unicode
ifPresent: [:a | evt at: 6 put: a first;
at: 5 put: a second].
KeyDecodeTable "ASCII character second"
at: ascii
ifPresent: [:a | evt at: 3 put: a first;
at: 5 put: a second]].
self queueEvent: evt.
self processKeyboardEvent: evt .
^self ].
"Handle all events other than Keyboard or Mouse."
self queueEvent: evt.
!
Item was changed:
----- Method: EventSensor>>rawMacOptionKeyPressed (in category 'modifier keys') -----
rawMacOptionKeyPressed
"Answer whether the option key on the Macintosh keyboard is being held down. Macintosh specific. Clients are discouraged from calling this directly, since it circumvents bert's attempt to eradicate option-key checks"
+ self deprecated: 'Use #optionKeyPressed instead.'.
+ ^ self optionKeyPressed!
- ^ self peekButtons anyMask: 32!
Item was changed:
----- Method: EventSensor>>shiftPressed (in category 'modifier keys') -----
shiftPressed
"Answer whether the shift key on the keyboard is being held down."
+ ^ self peekButtons anyMask: (2r0001 "shift" bitShift: MouseEvent numButtons)!
- ^ self peekButtons anyMask: (1 bitShift: MouseEvent numButtons)!
Item was changed:
+ (PackageInfo named: 'Kernel') postscript: 'EventSensor initialize.
+ EventSensor startUp.'!
- (PackageInfo named: 'Kernel') postscript: '"recompile all methods containing blocks with the wrong outerCode"
- (self systemNavigation allMethodsSelect: [:m| m literals anySatisfy: [:l| l isCompiledCode and: [l isCompiledBlock and: [l outerCode ~~ m]]]]) do:
- [:mr| mr actualClass recompile: mr selector]'!
Marcel Taeumel uploaded a new version of FFI-Tools to project FFI:
http://source.squeak.org/FFI/FFI-Tools-mt.21.mcz
==================== Summary ====================
Name: FFI-Tools-mt.21
Author: mt
Time: 20 April 2021, 5:17:02.045768 pm
UUID: 7def1cf3-e037-4cbc-ba03-c673cf7c0d27
Ancestors: FFI-Tools-mt.20
Complements FFI-Kernel-mt.120
=============== Diff against FFI-Tools-mt.20 ===============
Item was changed:
----- Method: ExternalData>>explorerContentsMetaFields (in category '*FFI-Tools') -----
explorerContentsMetaFields
+ "Skip _type because our external type is already in the basic explorer fields because it is an instance variable. Add _contentType for clarification."
+
+ ^ {
+ ObjectExplorerWrapper with: self containerType name: '_containerType' model: self.
+ ObjectExplorerWrapper with: self contentType name: '_contentType' model: self.
+ }!
- "Ignore because our type is already in the basic explorer fields because it is an instance variable."
- ^ #()!
Item was changed:
----- Method: ExternalData>>explorerContentsStructFields (in category '*FFI-Tools') -----
explorerContentsStructFields
"In case some data interpretation omitted to convert char*, which is a (null-terminated) C string, to Smalltalk string."
+ ^ (self isNull not and: [self containerType = ExternalType string]) ifFalse: [#()] ifTrue: [
- ^ (self isNull not and: [type = ExternalType string]) ifFalse: [#()] ifTrue: [
{ObjectExplorerWrapper
with: self fromCString
name: 'as C string'
model: self}]!
Item was changed:
----- Method: ExternalData>>hasContentsInExplorer (in category '*FFI-Tools') -----
hasContentsInExplorer
^ super hasContentsInExplorer
+ or: [self isNull not and: [self containerType = ExternalType string]]!
- or: [self isNull not and: [type = ExternalType string]]!
Marcel Taeumel uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel-mt.120.mcz
==================== Summary ====================
Name: FFI-Kernel-mt.120
Author: mt
Time: 20 April 2021, 5:16:20.338768 pm
UUID: 217ce793-267e-446b-9fea-61066a2b7741
Ancestors: FFI-Kernel-mt.119
ExternalData
- Clarify content-type and container-type (Thanks Nicolas!!! Yet there is still work to be done to support n-ary pointer types)
- Ensure that container-type is always a pointer type
- Remove unnecessary checks in #at:(put:)
- Make #from:to: retain the external address for empty results
- More code clean-up
ByteArray vs. ExternalAddress
- Clarify the role of byte arrays through #isInternalMemory
- Offer an #isNull: check using an external type to better support aliases on pointer-types
ExternalStructure
- Removes (my st00pid) notion of "stable representation" from ExternalStructure
- Updates print-string to surround name with brackets [...] whenever the handle is a ByteArray (i.e. #isInternalMemory) --- and drop all those "*" for external addresses, which is the default, I suppose
=============== Diff against FFI-Kernel-mt.119 ===============
Item was changed:
+ ----- Method: ByteArray>>isExternalAddress (in category '*FFI-Kernel-testing') -----
- ----- Method: ByteArray>>isExternalAddress (in category '*FFI-Kernel') -----
isExternalAddress
"Return true if the receiver describes the address of an object in the outside world"
^false!
Item was added:
+ ----- Method: ByteArray>>isInternalMemory (in category '*FFI-Kernel-testing') -----
+ isInternalMemory
+
+ ^ true!
Item was changed:
+ ----- Method: ByteArray>>isNull (in category '*FFI-Kernel-testing') -----
- ----- Method: ByteArray>>isNull (in category '*FFI-Kernel') -----
isNull
+ "Answer false since only pointers can be null, which is easy for external addresses but unknown for byte arrays without a proper external type for interpretation. See #isTypeAliasForPointer."
+
+ ^ false!
- "Answer false since only external addresses can be null"
- ^false!
Item was added:
+ ----- Method: ByteArray>>isNull: (in category '*FFI-Kernel-testing') -----
+ isNull: externalType
+ "Given the external type, answer whether the receiver holds all null bytes representing a null pointer."
+
+ "self assert: [self isInternalMemory]."
+ ^ externalType isTypeAliasForPointer
+ and: [externalType byteSize = self size]
+ and: [self allSatisfy: [:byte | byte = 0]]!
Item was added:
+ ----- Method: ExternalAddress>>isInternalMemory (in category 'testing') -----
+ isInternalMemory
+
+ ^ false!
Item was added:
+ ----- Method: ExternalAddress>>isNull: (in category 'testing') -----
+ isNull: externalType
+ "Overridden to make use of #isNull. This fails if the provided pointer size does not match, which indicates an inconsistency in the system's type objects for the current platform. See 'housekeeping' protocol in ExternalType."
+
+ self assert: [externalType pointerSize = self size].
+ ^ self isNull!
Item was changed:
----- Method: ExternalData>>asExternalStructure (in category 'converting') -----
asExternalStructure
self
+ assert: [self contentType referentClass includesBehavior: ExternalStructure]
- assert: [type referentClass includesBehavior: ExternalStructure]
description: 'Wrong type'.
+ ^ self contentType referentClass fromHandle: handle!
- ^ type referentClass fromHandle: handle!
Item was changed:
----- Method: ExternalData>>asExternalUnion (in category 'converting') -----
asExternalUnion
self
+ assert: [self contentType referentClass includesBehavior: ExternalUnion]
- assert: [type referentClass includesBehavior: ExternalUnion]
description: 'Wrong type'.
+ ^ self contentType referentClass fromHandle: handle!
- ^ type referentClass fromHandle: handle!
Item was added:
+ ----- Method: ExternalData>>asString (in category 'converting') -----
+ asString
+
+ ^ size
+ ifNil: [self fromCString]
+ ifNotNil: [self fromStringBounded]!
Item was changed:
----- Method: ExternalData>>assert:at: (in category 'accessing') -----
assert: expectedType at: index
self
+ assert: [self contentType = expectedType]
+ description: 'Wrong content type'.
- assert: [type = expectedType asPointerType]
- description: 'Wrong type'.
^ self at: index!
Item was changed:
----- Method: ExternalData>>assert:at:put: (in category 'accessing') -----
assert: expectedType at: index put: value
self
+ assert: [self contentType = expectedType]
+ description: 'Wrong content type'.
- assert: [type = expectedType]
- description: 'Wrong type'.
^ self at: index put: value!
Item was changed:
----- Method: ExternalData>>at: (in category 'accessing') -----
at: index
- self
- assert: [index = 1 or: [type isAtomic]]
- description: 'Should not read non-atomic pointer as array'.
-
((1 > index) or: [size notNil and: [index > size]])
ifTrue: [^ self errorSubscriptBounds: index].
+ ^ self contentType
- ^ type asNonPointerType
handle: handle
+ at: ((index-1) * self contentType byteSize) + 1!
- at: ((index-1) * type asNonPointerType byteSize) + 1!
Item was changed:
----- Method: ExternalData>>at:put: (in category 'accessing') -----
at: index put: value
- self
- assert: [index = 1 or: [type isAtomic]]
- description: 'Should not read non-atomic pointer as array'.
-
((1 > index) or: [size notNil and: [index > size]])
ifTrue: [^ self errorSubscriptBounds: index].
+ ^ self contentType
- ^ type asNonPointerType
handle: handle
+ at: ((index-1) * self contentType byteSize) + 1
- at: ((index-1) * type asNonPointerType byteSize) + 1
put: value!
Item was added:
+ ----- Method: ExternalData>>containerType (in category 'accessing - types') -----
+ containerType
+
+ ^ type!
Item was added:
+ ----- Method: ExternalData>>contentType (in category 'accessing - types') -----
+ contentType
+
+ self flag: #todo. "mt: For n-ary pointer types, we typically just want to reducy arity by one."
+ ^ type asNonPointerType!
Item was changed:
+ ----- Method: ExternalData>>externalType (in category 'accessing - types') -----
- ----- Method: ExternalData>>externalType (in category 'accessing') -----
externalType
^ type!
Item was changed:
----- Method: ExternalData>>from:to: (in category 'accessing') -----
from: firstIndex to: lastIndex
+ "Only copy data if already in object memory, that is, as byte array. Only check size if configured."
- "Only copy data if already in object memory, that is, as byte array."
+ | byteOffset numElements byteSize newType |
- | byteOffset numElements byteSize |
- lastIndex < firstIndex ifTrue: [
- ^ (ExternalData fromHandle: #[] type: type)
- size: 0; yourself].
-
((1 > firstIndex) or: [size notNil and: [lastIndex > size]])
ifTrue: [^ self errorSubscriptBounds: lastIndex].
+ byteOffset := ((firstIndex-1) * self contentType byteSize)+1.
- byteOffset := ((firstIndex-1) * type asNonPointerType byteSize)+1.
numElements := lastIndex - firstIndex + 1.
+ byteSize := numElements * self contentType byteSize.
+
+ "For portions of a null-terminated C string, change the type from char* to byte* to avoid confusion."
+ newType := self containerType = ExternalType string
+ ifTrue: [ExternalType byte asPointerType]
+ ifFalse: [self containerType "No change"].
- byteSize := size * type asNonPointerType byteSize.
+ ^ lastIndex < firstIndex
+ ifTrue: [
+ handle isExternalAddress
+ ifTrue: [(ExternalData
+ fromHandle: handle + (byteOffset - 1) "Keep pointer."
+ type: newType) size: 0; yourself]
+ ifFalse: [(ExternalData
+ fromHandle: #[] "Empty memory"
+ type: newType) size: 0; yourself]]
+ ifFalse: [
+ handle isExternalAddress
+ ifTrue: [(ExternalData
+ fromHandle: handle + (byteOffset - 1)
+ type: newType) size: numElements; yourself]
+ ifFalse: [(ExternalData
+ fromHandle: (handle copyFrom: byteOffset to: byteOffset+byteSize)
+ type: newType) size: numElements; yourself]]!
- ^ handle isExternalAddress
- ifTrue: [(ExternalData
- fromHandle: handle + byteOffset - 1
- type: type) size: numElements; yourself]
- ifFalse: [(ExternalData
- fromHandle: (handle copyFrom: byteOffset to: byteOffset+byteSize)
- type: type) size: numElements; yourself]!
Item was changed:
+ ----- Method: ExternalData>>fromCString (in category 'converting - support') -----
- ----- Method: ExternalData>>fromCString (in category 'converting') -----
fromCString
"Assume that the receiver represents a C string and convert it to a Smalltalk string. hg 2/25/2000 14:18"
| stream index char |
self
+ assert: [self containerType = ExternalType string]
+ description: 'Wrong content type'.
- assert: [self externalType = ExternalType string]
- description: 'Wrong type'.
stream := WriteStream on: String new.
index := 1.
[(char := self at: index) = 0 asCharacter] whileFalse: [
stream nextPut: char.
index := index + 1].
^stream contents!
Item was changed:
+ ----- Method: ExternalData>>fromCStrings (in category 'converting - support') -----
- ----- Method: ExternalData>>fromCStrings (in category 'converting') -----
fromCStrings
"Assume that the receiver represents a set of C strings and is teerminated by a empty string and convert it to a Smalltalk ordered collection of strings"
| stream index char strings str |
type isPointerType ifFalse: [self error: 'External object is not a pointer type.'].
+ self flag: #bogus. "mt: This format seems to be rather specific to some library. There would normally be pointers to pointers for such a structure. Or does the C standard mention such a format somehow? 'abcd\0efg\0hijklmnopq\0rstuvwxyz\0\0' ??? "
strings := OrderedCollection new.
index := 1.
[
stream := WriteStream on: String new.
[(char := handle unsignedCharAt: index) = 0 asCharacter]
whileFalse: [
stream nextPut: char.
index := index + 1
].
str := stream contents.
strings addLast: str.
str size = 0
] whileFalse.
^strings!
Item was added:
+ ----- Method: ExternalData>>fromStringBounded (in category 'converting - support') -----
+ fromStringBounded
+ "Read byte* as bounded string. You have to set a #size first."
+
+ | offset step |
+ self
+ assert: [self contentType = ExternalType byte]
+ description: 'Wrong content type'.
+
+ self sizeCheck.
+
+ offset := 1.
+ step := self contentType byteSize.
+
+ ^ String streamContents: [:s |
+ size timesRepeat: [
+ s nextPut: (handle unsignedCharAt: offset).
+ offset := offset + step]]!
Item was changed:
----- Method: ExternalData>>getExternalData (in category 'accessing - external structures') -----
getExternalData
+ "Reads all bytes into object memory. Note that this does not flatten all bytes into a single array by repeatedly calling it. It does just work once for an external address."
- "Reads all bytes into object memory."
| data |
handle isExternalAddress ifFalse: [^ self].
self sizeCheck.
+ data := ByteArray new: size * self contentType byteSize.
- data := ByteArray new: size * type asNonPointerType byteSize.
1 to: data size do: [:index |
data unsignedByteAt: index put: (handle unsignedByteAt: index)].
^ (ExternalData
fromHandle: data
+ type: self contentType)
- type: type asNonPointerType)
size: size!
Item was changed:
----- Method: ExternalData>>getExternalStructure (in category 'accessing - external structures') -----
getExternalStructure
"Reads an external structure from this external data. If the receiver's handle is an external address, the structure's fields will be copied into object memory. Use #asExternalStructure if you want to avoid this copy."
self
+ assert: [self contentType referentClass includesBehavior: ExternalStructure]
+ description: 'Wrong content type'.
- assert: [type referentClass includesBehavior: ExternalStructure]
- description: 'Wrong type'.
^ handle isExternalAddress
+ ifTrue: [self getExternalData asExternalStructure]
+ ifFalse: [self asExternalStructure]!
- ifTrue: [self getExternalData getExternalStructure]
- ifFalse: [type referentClass fromHandle: handle]!
Item was changed:
----- Method: ExternalData>>getExternalUnion (in category 'accessing - external structures') -----
getExternalUnion
"Reads an external union from this external data. If the receiver's handle is an external address, the union's fields will be copied into object memory. Use #asExternalUnion if you want to avoid this copy."
self
+ assert: [self contentType referentClass includesBehavior: ExternalUnion]
+ description: 'Wrong content type'.
- assert: [type referentClass includesBehavior: ExternalUnion]
- description: 'Wrong type'.
^ handle isExternalAddress
+ ifTrue: [self getExternalData asExternalUnion]
+ ifFalse: [self asExternalUnion]!
- ifTrue: [self getExternalData getExternalUnion]
- ifFalse: [type referentClass fromHandle: handle]!
Item was added:
+ ----- Method: ExternalData>>printContentTypeOn: (in category 'printing') -----
+ printContentTypeOn: stream
+
+ stream
+ nextPut: $<;
+ print: self contentType;
+ nextPut: $>.!
Item was changed:
----- Method: ExternalData>>printOn: (in category 'printing') -----
printOn: stream
super printOn: stream.
+ self printContentTypeOn: stream.!
- self printTypeOn: stream.!
Item was removed:
- ----- Method: ExternalData>>printPointerOn: (in category 'printing') -----
- printPointerOn: stream
- "Ignore since it is part of the type, e.g. char* or int[] etc."!
Item was removed:
- ----- Method: ExternalData>>printTypeOn: (in category 'printing') -----
- printTypeOn: stream
-
- stream
- nextPut: $<;
- print: type;
- nextPut: $>.!
Item was changed:
----- Method: ExternalData>>setHandle:type: (in category 'private') -----
setHandle: aHandle type: aType
handle := aHandle.
+ type := aType asPointerType.!
- type := aType.!
Item was changed:
----- Method: ExternalStructure>>externalType (in category 'accessing') -----
externalType
+ ^ self class externalType!
- | type |
- self flag: #ffiDesignSmell. "mt: Note that type aliases to pointer types store pointers via handle as ByteArray, not ExternalAddress."
- type := self class externalType.
- ^ handle class == ExternalAddress
- ifTrue: [type asPointerType]
- ifFalse: [type]!
Item was removed:
- ----- Method: ExternalStructure>>hasStableRepresentation (in category 'testing') -----
- hasStableRepresentation
- "Answers whether the contents of this structure have a stable representation in memory. Basically, every handle that is not a ByteArray can be considered a stable representation. The primary examples would be handles that are 'nil' or an ExternalAddress. Additionally, there can be type aliases to atomic types, which then store the Smalltalk object, e.g. Integer or Float, for such an atomic, e.g. long or float, directly in 'handle'. Those Smalltalk objects are typically immediate (e.g., integers, characters). Note that strings map to char*, which is an (atomic) pointer type and thus accessible only through an ExternalData, which itself always holds an external address as its handle."
-
- ^ handle class ~~ ByteArray!
Item was changed:
----- Method: ExternalStructure>>isNull (in category 'testing') -----
isNull
+ handle isInternalMemory
+ ifTrue: [^ handle isNull: self externalType].
+ handle isExternalAddress
+ ifTrue: [^ handle isNull].
+ ^ handle isNil!
- self flag: #ffiDesignSmell. "Type aliases to atomic types store data via handle as Smalltalk object. Consequently, #isNull and #isExternalAddress must not be sent to 'handle' without care. Actually, #isExternalAddress is rather useless at the moment."
-
- handle class == ExternalAddress ifTrue: [^ handle isNull].
- self hasStableRepresentation ifTrue: [^ handle isNil].
-
- self flag: #ffiDesignSmell. "Type aliases to pointer types store pointers via handle as ByteArray, not ExternalAddress (like regular pointer types). Consequently, it is tricky to detect a NULL pointer in the general sense. Here, try to check for #isTypeAliasForPointer and only then check for all bytes being 0 in the byte array."
-
- "self assert: [self externalType isTypeAliasForPointer => [handle class == ByteArray]]."
- ^ self externalType isTypeAliasForPointer
- and: [handle allSatisfy: [:byte | byte = 0 ]]!
Item was changed:
----- Method: ExternalStructure>>printIdentityOn: (in category 'printing') -----
printIdentityOn: stream
+ "Reveal information about this external object's identity so that users can quickly assess the need for inspecting its contents. Users can also infer lifetime properties and consider those when passing this object around in the system."
- "Reveal information about this external object's identity so that users can quickly assess the need for inspecting its contents. Users can also infer lifetime properties and consider those when passing this object around in the system. For example, objects that are created on-the-fly when accessing fields via an external address may be of less value compared to objects that are actually hold at those external addresses. See #printPointerOn:."
handle ifNil: [
^ stream nextPutAll: '<UNDEFINED>'].
self isNull ifTrue: [
+ ^ stream nextPutAll: '<NULL>'].!
- ^ stream nextPutAll: '<NULL>'].
-
- self hasStableRepresentation
- ifTrue: ["Object has a stable representation. No need to expose its memory address in the UI."
- ^ self]
- ifFalse: ["Inform the user that this data was copied into object memory."
- ^ stream nextPut: $<; print: handle identityHash; nextPut: $>].
- !
Item was changed:
----- Method: ExternalStructure>>printOn: (in category 'printing') -----
printOn: stream
+ handle ifNil: [stream nextPutAll: '? '].
+ handle isInternalMemory ifTrue: [stream nextPutAll: '['].
+
super printOn: stream.
+ handle ifNil: [stream nextPutAll: ' ?'].
+ handle isInternalMemory ifTrue: [stream nextPutAll: ']'].
+
- self printPointerOn: stream.
self printIdentityOn: stream.!
Item was removed:
- ----- Method: ExternalStructure>>printPointerOn: (in category 'printing') -----
- printPointerOn: stream
- "Indicate whether this structure points to an external address or whether its contents got copied into a byte array in object memory."
-
- handle ifNil: [^ stream nextPutAll: '<UNDEFINED>'].
-
- handle class == ExternalAddress
- ifTrue: [stream nextPutAll: '*'].!
Item was added:
+ ----- Method: ExternalType>>isTypeAlias (in category 'testing') -----
+ isTypeAlias
+
+ ^ false!
Item was added:
+ ----- Method: ExternalType>>isTypeAliasForPointer (in category 'testing') -----
+ isTypeAliasForPointer
+
+ ^ false!
Item was added:
+ ----- Method: Object>>isExternalAddress (in category '*FFI-Kernel') -----
+ isExternalAddress
+ "Return true if the receiver describes the address of an object in the outside world. NOTE that this backstop is in Object because atomic types store actual objects (e.g., numbers) as their handle."
+
+ ^ false!
Item was added:
+ ----- Method: Object>>isInternalMemory (in category '*FFI-Kernel') -----
+ isInternalMemory
+ "Return true if the receiver describes a region of memory (within Squeak's object memory) to be interpreted (e.g., as external structure, pointer, ...). NOTE that this backstop is in Object because atomic types store actual objects (e.g., numbers) as their handle."
+
+ ^ false!
Nicolas Cellier uploaded a new version of ToolBuilder-Kernel to project The Trunk:
http://source.squeak.org/trunk/ToolBuilder-Kernel-nice.141.mcz
==================== Summary ====================
Name: ToolBuilder-Kernel-nice.141
Author: nice
Time: 20 April 2021, 11:03:36.052217 am
UUID: b33a2801-5d76-41b6-a323-4c4a13ff185a
Ancestors: ToolBuilder-Kernel-eem.140
The handlers for other exception should not be deactivated while redirecting ProgressInitiationException thru sendNotificationsTo: directive.
=============== Diff against ToolBuilder-Kernel-eem.140 ===============
Item was changed:
----- Method: ProgressInitiationException>>sendNotificationsTo: (in category 'initialize-release') -----
sendNotificationsTo: aNewBlock
+ self reactivateHandlers; resumeUnchecked: (
- self resume: (
workBlock value: [ :barVal |
aNewBlock value: minVal value: maxVal value: barVal
]
)
!