diff --git a/.gitignore b/.gitignore index 7a301693c7e..7ad1add4d8e 100644 --- a/.gitignore +++ b/.gitignore @@ -20,11 +20,12 @@ encryptedstack.cpp encryptedstack.h stacksecurity_encrypted.cpp util/perfect/perfect.linux-* -extensions/*/*/*.xml -extensions/*/*/*.lci -extensions/*/*/*.lcm -extensions/*/*/*.lce -extensions/*/*/api.lcdoc +**/extensions/*/*/*.xml +**/extensions/*/*/*.lci +**/extensions/*/*/*.lcm +**/extensions/*/*/*.lce +**/extensions/*/*/api.lcdoc +*/src/.lci/* # Compiled source and intermediates # ################### diff --git a/Makefile.common b/Makefile.common index 81939644dfa..864c64bb688 100644 --- a/Makefile.common +++ b/Makefile.common @@ -43,6 +43,10 @@ guess_linux_arch_script := \ case `uname -p` in \ x86_64) echo x86_64 ;; \ x86|i*86) echo x86 ;; \ + unknown) case `uname -m` in \ + x86_64) echo x86_64 ;; \ + x86|i*86) echo x86 ;; \ + esac ;; \ esac guess_linux_arch := $(shell $(guess_linux_arch_script)) diff --git a/docs/development/testing.md b/docs/development/testing.md index 2be01270b59..9cf06a10d8b 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -61,11 +61,13 @@ Before running each test command, the test framework inserts a test library stac * `TestAssert pDescription, pExpectTrue`: Make a test assertion. The test is recorded as a failure if *pExpectTrue* is false. *pDescription* should be a short string that describes the test (e.g. "clipboard is clear"). * `TestSkip pDescription, pReasonSkipped`: Record a test as having been skipped. *pReasonSkipped* should be a short explanation of why the test was skipped (e.g. "not supported on Windows"). * `TestSkipIf pRequirement, pOptions`: Skip a test if the requirements -are met. `pOptions` varies depending on the `pRequirement` enum. The -following requirements are implemented: - - `ide` - the IDE repo is available. No options. +are met. `pOptions` varies depending on the `pRequirement` enum (if no +options are explicitly specified then no options are available for that +particular `pRequirement`. The following requirements are implemented: + - `ide` - the IDE repo is available - `lcb` - LCB compilation supported - - `docs` - the docs are available. No options. + - `docs` - the docs are available + - `standalone` - the test is running in the standalone test runner - `securityPermissions` - Option `set` to skip if a test should not set the `securityPermissions` - `platform` - options are comma delimited platform strings @@ -73,6 +75,19 @@ following requirements are implemented: - `stack` - options are comma delimited stack names to test if they are available - `environment` - options are comma delimited environment strings + - `clipboard` - access to the clipboard is available + - `wait` - the `wait` command is available and works as expected + - `security` - the security module is available + - `write` - write access to the filesystem is available + - `ui` - the test is running in a graphical environment (as opposed + to the command line) + - `desktop` - the test is running on a desktop computer + - `mobile` - the test is running on a mobile device + - `external` - an external module can be loaded/used. Options are a + comma delimited list of external module names + - `database` - an database module can be loaded/used. Options are a + comma delimited list of external module names + - `jvm` - the Java Virtual Machine is available * `TestSkipIfNot pRequirement, pOptions`: Skip a test if the requirements are not met. Requirements and options are the same as for `TestSkipIf`. diff --git a/docs/dictionary/command/androidRequestPermission.lcdoc b/docs/dictionary/command/androidRequestPermission.lcdoc new file mode 100644 index 00000000000..07e5f1ec831 --- /dev/null +++ b/docs/dictionary/command/androidRequestPermission.lcdoc @@ -0,0 +1,65 @@ +Name: androidRequestPermission + +Type: command + +Syntax: androidRequestPermission + +Summary: +Displays a dialog showing a permission request for . If a user has already granted permission for , this command does nothing. + +Introduced: 9.0.1 + +OS: android + +Platforms: mobile + +Example: +local tCameraPermissionGranted +put androidHasPermission("android.permission.CAMERA") into tCameraPermissionGranted +if not tCameraPermissionGranted then + androidRequestPermission "android.permission.CAMERA" +end if +put androidHasPermission("android.permission.CAMERA") into tCameraPermissionGranted +if not tCameraPermissionGranted then + answer "This app is not permitted to access the device camera. You can change this" && \ + "in the Settings app." +end if + + +Parameters: +permissionName (enum): +The name of the permission to request. + +- "android.permission.READ_CALENDAR": permission to allow an application to read the device's calendar. +- "android.permission.WRITE_CALENDAR": permission to allow an application to write to the device's calendar. +- "android.permission.CAMERA": permission to allow an application to access the device's camera. +- "android.permission.ACCESS_COARSE_LOCATION": permission to allow an application to access the device's coarse location. +- "android.permission.ACCESS_FINE_LOCATION": permission to allow an application to access the device's fine location. +- "android.permission.READ_CONTACTS": permission to allow an application to read data from the device's contacts. +- "android.permission.WRITE_CONTACTS": permission to allow an application to write date to the device's contacts. +- "android.permission.GET_ACCOUNTS": permission to allow an application to access to the list of accounts in the Accounts Service. +- "android.permission.RECORD_AUDIO": permission to allow an application to allow an application to record audio. +- "android.permission.READ_EXTERNAL_STORAGE": permission to allow an application to read data from the device's external storage. +- "android.permission.WRITE_EXTERNAL_STORAGE": permission to allow an application to write data to the device's external storage. +- "android.permission.READ_PHONE_STATE": permission to allow an application to access phone state, including the phone number of the device, current cellular network information, the status of any ongoing calls, and a list of any PhoneAccounts registered on the device. +- "android.permission.READ_PHONE_NUMBERS": permission to allow an application to access the device's phone number(s). +- "android.permission.CALL_PHONE": permission to allow an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call. +- "android.permission.ANSWER_PHONE_CALLS": permission to allow an application to answer an incoming phone call. +- "android.permission.READ_CALL_LOG": permission to allow an application to read the user's call log. +- "android.permission.WRITE_CALL_LOG": permission to allow an application to write to the user's call log. +- "android.permission.ADD_VOICEMAIL": permission to allow an application to add voicemails into the system. +- "android.permission.USE_SIP": permission to allow an application to use SIP service. +- "android.permission.PROCESS_OUTGOING_CALLS": permission to allow an application to see the number being dialed during an outgoing call with the option to redirect the call to a different number or abort the call altogether. +- "android.permission.SEND_SMS": permission to allow an application to send SMS messages. +- "android.permission.RECEIVE_SMS": permission to allow an application to receive SMS messages +- "android.permission.READ_SMS": permission to allow an application to read SMS messages. +- "android.permission.RECEIVE_WAP_PUSH": permission to allow an application to receive WAP push messages. +- "android.permission.RECEIVE_MMS": permission to allow an application to receive MMS messages. +- "android.permission.BODY_SENSORS": permission to allow an application to access data from sensors that the user uses to measure what is happening inside his/her body, such as heart rate. + + + +Description: +Use the command to request permission for from the user. + +>*Note:* Permission names are case sensitive. diff --git a/docs/dictionary/command/mobilePickMedia.lcdoc b/docs/dictionary/command/mobilePickMedia.lcdoc index 0306ecb4163..9f0852ab55f 100644 --- a/docs/dictionary/command/mobilePickMedia.lcdoc +++ b/docs/dictionary/command/mobilePickMedia.lcdoc @@ -76,15 +76,17 @@ to select multiple items on iOS, the user has to select the Done button, once all of the selections have been made. The remaining arguments specify the type of media items the user should -> be allowed to select from the iPod library. On Android, it is not -> necessary to specify any arguments. The application that is launched -> is in control of the audio media types that are to be selected. +be allowed to select from the iPod library. On Android, it is not +necessary to specify any arguments. The application that is launched +is in control of the audio media types that are to be selected. >*Note:* does not return anything for m4p files or > iCloud files that are not physically on the device, even if they are > displayed in the picker itself. + >*Note:* On iOS, returns references to the iTunes data > store on the iOS device. These media references can only be played > with the command. + >*Note:* is not available on the iOS simulator. References: mobilePlaySoundOnChannel (command), mobilePick (command), diff --git a/docs/dictionary/command/mobilePickPhoto.lcdoc b/docs/dictionary/command/mobilePickPhoto.lcdoc index 5bfe65a855b..636c0e415ca 100644 --- a/docs/dictionary/command/mobilePickPhoto.lcdoc +++ b/docs/dictionary/command/mobilePickPhoto.lcdoc @@ -23,15 +23,13 @@ mobilePickPhoto "front camera", 250, 250 Parameters: source (enum): -The source for the image. One of: +The source for the image. -- "library" -- "album" -- "camera" - - -- "rear camera": iOS only -- "front camera": iOS only +- "library": +- "album": +- "camera": +- "rear camera": iOS only +- "front camera": iOS only maxwidth: diff --git a/docs/dictionary/command/mobilePlaySoundOnChannel.lcdoc b/docs/dictionary/command/mobilePlaySoundOnChannel.lcdoc index 5b108ba4c66..c27b7814e4c 100644 --- a/docs/dictionary/command/mobilePlaySoundOnChannel.lcdoc +++ b/docs/dictionary/command/mobilePlaySoundOnChannel.lcdoc @@ -33,8 +33,6 @@ channel: The name of the channel to play the sound on. type (enum): -One of: - - "now": The sound is played immediately, replacing any current sound (and queued sound) on the channel. - "next": The sound is queued to play immediately after the current diff --git a/docs/dictionary/command/mobileSetLocationHistoryLimit.lcdoc b/docs/dictionary/command/mobileSetLocationHistoryLimit.lcdoc index e438f18cf29..25aaa4b2e5e 100644 --- a/docs/dictionary/command/mobileSetLocationHistoryLimit.lcdoc +++ b/docs/dictionary/command/mobileSetLocationHistoryLimit.lcdoc @@ -57,21 +57,19 @@ sample provided with the event (which will always be the last sample in the history). e.g. on locationChanged - - local tHistory - put mobileGetLocationHistory() into tHistory - repeat for each element tSample in tHistory - - processLocationChanged tSample - - end repeat + local tHistory + put mobileGetLocationHistory() into tHistory + repeat for each element tSample in tHistory + processLocationChanged tSample + end repeat end locationChanged References: mobileStopTrackingSensor (command), -mobileStartTrackingSensor (command), mobileGetLocationHistory (function), +mobileStartTrackingSensor (command), +mobileGetLocationHistory (function), mobileGetLocationHistoryLimit (function), mobileSensorAvailable (function), mobileSensorReading (function), -mobileLocationAuthorizationStatus (function), locationChanged (message), -trackingError (message) +mobileLocationAuthorizationStatus (function), +locationChanged (message), trackingError (message) diff --git a/docs/dictionary/command/mobileStartTrackingSensor.lcdoc b/docs/dictionary/command/mobileStartTrackingSensor.lcdoc index dab95fc0c77..3137ccfea4d 100644 --- a/docs/dictionary/command/mobileStartTrackingSensor.lcdoc +++ b/docs/dictionary/command/mobileStartTrackingSensor.lcdoc @@ -33,9 +33,9 @@ loosely (enum): How accurate the readings from sensors should be. The default value is false -- "true": readings are determined without using accurate(but power +- "true": readings are determined without using accurate (but power consuming) sources such as GPS. -- "false": readings are determined using accurate(but power consuming) +- "false": readings are determined using accurate (but power consuming) sources such as GPS. diff --git a/docs/dictionary/function/androidHasPermission.lcdoc b/docs/dictionary/function/androidHasPermission.lcdoc new file mode 100644 index 00000000000..48219cce0e2 --- /dev/null +++ b/docs/dictionary/function/androidHasPermission.lcdoc @@ -0,0 +1,66 @@ +Name: androidHasPermission + +Type: function + +Syntax: androidHasPermission() + +Summary: +Returns if permission has been granted by the user. + +Introduced: 9.0.1 + +OS: android + +Platforms: mobile + +Example: +local tLocationPermissionGranted +put androidHasPermission("android.permission.ACCESS_FINE_LOCATION") into tLocationPermissionGranted +if not tLocationPermissionGranted then + androidRequestPermission "android.permission.ACCESS_FINE_LOCATION" +end if +if not tLocationPermissionGranted then + answer "This app is not permitted to access the device location. You can change this" && \ + "in the Settings app." +end if + + +Parameters: +permissionName (enum): +The name of the permission to request. + +- "android.permission.READ_CALENDAR": permission to allow an application to read the device's calendar. +- "android.permission.WRITE_CALENDAR": permission to allow an application to write to the device's calendar. +- "android.permission.CAMERA": permission to allow an application to access the device's camera. +- "android.permission.ACCESS_COARSE_LOCATION": permission to allow an application to access the device's coarse location. +- "android.permission.ACCESS_FINE_LOCATION": permission to allow an application to access the device's fine location. +- "android.permission.READ_CONTACTS": permission to allow an application to read data from the device's contacts. +- "android.permission.WRITE_CONTACTS": permission to allow an application to write date to the device's contacts. +- "android.permission.GET_ACCOUNTS": permission to allow an application to access to the list of accounts in the Accounts Service. +- "android.permission.RECORD_AUDIO": permission to allow an application to allow an application to record audio. +- "android.permission.READ_EXTERNAL_STORAGE": permission to allow an application to read data from the device's external storage. +- "android.permission.WRITE_EXTERNAL_STORAGE": permission to allow an application to write data to the device's external storage. +- "android.permission.READ_PHONE_STATE": permission to allow an application to access phone state, including the phone number of the device, current cellular network information, the status of any ongoing calls, and a list of any PhoneAccounts registered on the device. +- "android.permission.READ_PHONE_NUMBERS": permission to allow an application to access the device's phone number(s). +- "android.permission.CALL_PHONE": permission to allow an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call. +- "android.permission.ANSWER_PHONE_CALLS": permission to allow an application to answer an incoming phone call. +- "android.permission.READ_CALL_LOG": permission to allow an application to read the user's call log. +- "android.permission.WRITE_CALL_LOG": permission to allow an application to write to the user's call log. +- "android.permission.ADD_VOICEMAIL": permission to allow an application to add voicemails into the system. +- "android.permission.USE_SIP": permission to allow an application to use SIP service. +- "android.permission.PROCESS_OUTGOING_CALLS": permission to allow an application to see the number being dialed during an outgoing call with the option to redirect the call to a different number or abort the call altogether. +- "android.permission.SEND_SMS": permission to allow an application to send SMS messages. +- "android.permission.RECEIVE_SMS": permission to allow an application to receive SMS messages +- "android.permission.READ_SMS": permission to allow an application to read SMS messages. +- "android.permission.RECEIVE_WAP_PUSH": permission to allow an application to receive WAP push messages. +- "android.permission.RECEIVE_MMS": permission to allow an application to receive MMS messages. +- "android.permission.BODY_SENSORS": permission to allow an application to access data from sensors that the user uses to measure what is happening inside his/her body, such as heart rate. + +Returns(boolean): +True if permission has been granted, false otherwise. + + +Description: +Use the function to find out if permission has been granted by the user. + +>*Note:* Permission names are case sensitive. diff --git a/docs/dictionary/function/androidPermissionExists.lcdoc b/docs/dictionary/function/androidPermissionExists.lcdoc new file mode 100644 index 00000000000..46f1341b896 --- /dev/null +++ b/docs/dictionary/function/androidPermissionExists.lcdoc @@ -0,0 +1,33 @@ +Name: androidPermissionExists + +Type: function + +Syntax: androidPermissionExists() + +Summary: +Returns true if is a valid Android permission name + +Introduced: 9.0.1 + +OS: android + +Platforms: mobile + +Example: + +if not androidPermissionExists(pPermission) then + answer pPermission && "is not a valid permission name" +end if + + +Parameters: +permissionName (string): +The name of the permission to check. + +Returns(boolean): +True if is a valid Android permission name, false otherwise. + +Description: +Use the function to check if is a valid Android permission name. + +>*Note:* Permission names are case sensitive. diff --git a/docs/dictionary/function/mobileGetLocationHistory.lcdoc b/docs/dictionary/function/mobileGetLocationHistory.lcdoc index 16c27d2a1db..5cd347a1335 100644 --- a/docs/dictionary/function/mobileGetLocationHistory.lcdoc +++ b/docs/dictionary/function/mobileGetLocationHistory.lcdoc @@ -19,51 +19,31 @@ local tHistory put mobileGetLocationHistory() into tHistory Returns (array): -A numerically keyed of all accumulated samples +A numerically keyed, nested of all accumulated samples since the last time it was called with lower indices being older -samples. Calling the function clears the internal history. The default -history limit is 1 - meaning that only one sample is ever kept at a +samples. Each index of the has the following keys: + - "horizontal accuracy": the maximum error in meters of the +position indicated by longitude and latitude + - "latitude": the latitude of the current location, measured in degrees +relative to the equator. Positive values indicate positions in the +Northern Hemisphere, negative values in the Southern. + - "longitude": the longitude of the current location, measured in +degrees relative to the zero meridian. Positive values extend east +of the meridian, negative values extend west. + - "altitude": the distance in meters of the height of the device +relative to sea level. Positive values extend upward of sea level, +negative values downward. + - "timestamp": the time at which the measurement was taken, in +seconds since 1970. + +Calling the function clears the internal history. The default +history limit is 1, meaning that only one sample is ever kept at a time. If an application wants historical access to all samples, then it should set the location history limit using the - to the maximum number of + to the maximum number of samples it ever wants to record, or 0 to record the entire history (between calls to ). -{ - -- (integer) : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- : -- (real) : -- : -- : -- : - -} - Description: System events may occur more frequently than the @@ -82,14 +62,11 @@ sample provided with the event (which will always be the last sample in the history). e.g. on locationChanged - - local tHistory - put mobileGetLocationHistory() into tHistory - repeat for each element tSample in tHistory - - processLocationChanged tSample - - end repeat + local tHistory + put mobileGetLocationHistory() into tHistory + repeat for each element tSample in tHistory + processLocationChanged tSample + end repeat end locationChanged @@ -97,6 +74,6 @@ References: mobileSetLocationHistoryLimit (command), mobileStopTrackingSensor (command), mobileStartTrackingSensor (command), mobileGetLocationHistoryLimit (function), mobileSensorAvailable (function), mobileSensorReading (function), -mobileLocationAuthorizationStatus (function), locationChanged (message), -trackingError (message) +mobileLocationAuthorizationStatus (function), +locationChanged (message), trackingError (message) diff --git a/docs/dictionary/function/mobileOrientation.lcdoc b/docs/dictionary/function/mobileOrientation.lcdoc index 40a138ae97d..b66653f0287 100644 --- a/docs/dictionary/function/mobileOrientation.lcdoc +++ b/docs/dictionary/function/mobileOrientation.lcdoc @@ -19,8 +19,7 @@ Example: put mobileOrientation() into theInterfaceOrientation Returns (enum): -The function returns one of - +The function returns a string value. - portrait: the device is being held upward with the home button at the bottom - portrait upside down: the device is being held upward with the home diff --git a/docs/dictionary/function/mobilePixelDensity.lcdoc b/docs/dictionary/function/mobilePixelDensity.lcdoc index e7d06bdea33..002e78e1837 100644 --- a/docs/dictionary/function/mobilePixelDensity.lcdoc +++ b/docs/dictionary/function/mobilePixelDensity.lcdoc @@ -32,8 +32,8 @@ density on Android. The function returns the scale of the device screen (relative to a non-Retinal display on iOS), this is 2 on a retina -display and 1 otherwise. On Android this function return the Display -Metrics. +display and 1 otherwise. On Android this function returns the display +metrics. References: iphoneUseDeviceResolution (command), iphoneDeviceScale (function), iphoneDeviceResolution (function) diff --git a/docs/dictionary/function/mobilePreferredLanguages.lcdoc b/docs/dictionary/function/mobilePreferredLanguages.lcdoc index dbc864ce44d..e09b04f3254 100644 --- a/docs/dictionary/function/mobilePreferredLanguages.lcdoc +++ b/docs/dictionary/function/mobilePreferredLanguages.lcdoc @@ -23,9 +23,12 @@ answer "The user prefers" && line 1 of tLanguages Returns: The function returns a list of language tags in order of user preference, highest first, one per line. For example: + EN + FR -DE. + +DE Description: Use to get a list of preferred user diff --git a/docs/dictionary/function/mobileSoundChannelStatus.lcdoc b/docs/dictionary/function/mobileSoundChannelStatus.lcdoc index 8638ea77fa8..bab87b21f92 100644 --- a/docs/dictionary/function/mobileSoundChannelStatus.lcdoc +++ b/docs/dictionary/function/mobileSoundChannelStatus.lcdoc @@ -22,7 +22,7 @@ Example: put mobileSoundChannelStatus() into tChannelStatus Returns (enum): -The function returns one of: +The function returns a string value. - stopped: there is no sound currently playing, nor any sound scheduled to be playing diff --git a/docs/dictionary/function/round.lcdoc b/docs/dictionary/function/round.lcdoc index b1e5cb98588..89fd57ab389 100644 --- a/docs/dictionary/function/round.lcdoc +++ b/docs/dictionary/function/round.lcdoc @@ -47,12 +47,12 @@ the up if positive, down if . (To round off numbers without introducing any statistical upward bias, use the instead.) -A positive indicates a place to the right of the , and a indicates a place to the left. For -example, 1 rounds off to the nearest tenth, 2 rounds off to the nearest -hundredth, -1 rounds off by ten, and so on. If you don't specify a -, zero is used, meaning that the is rounded off to a -whole number. +A positive indicates a place to the right of the +, and a indicates a place to the +left. For example, 1 rounds off to the nearest tenth, 2 rounds off to +the nearest hundredth, -1 rounds off by ten, and so on. If you don't +specify a , zero is used, meaning that the is +rounded off to a whole number. >*Note:* The is equivalent to > "round" . diff --git a/docs/lcb/notes/21507.md b/docs/lcb/notes/21507.md new file mode 100644 index 00000000000..5e6dc61164d --- /dev/null +++ b/docs/lcb/notes/21507.md @@ -0,0 +1 @@ +# [21507] Fix incorrect parameter order in com.livecode.math atan2 function diff --git a/docs/notes/bugfix-18395.md b/docs/notes/bugfix-18395.md new file mode 100644 index 00000000000..6956ec53449 --- /dev/null +++ b/docs/notes/bugfix-18395.md @@ -0,0 +1 @@ +# Fix return key event handling in fields on Android \ No newline at end of file diff --git a/docs/notes/bugfix-21396.md b/docs/notes/bugfix-21396.md new file mode 100644 index 00000000000..c3b4faed0b7 --- /dev/null +++ b/docs/notes/bugfix-21396.md @@ -0,0 +1 @@ +# Fix crash on startup in iOS 12 beta \ No newline at end of file diff --git a/docs/notes/bugfix-21417.md b/docs/notes/bugfix-21417.md new file mode 100644 index 00000000000..e0c98c759f6 --- /dev/null +++ b/docs/notes/bugfix-21417.md @@ -0,0 +1 @@ +# Don't include any externals in emscripten standalones diff --git a/docs/notes/bugfix-21434.md b/docs/notes/bugfix-21434.md new file mode 100644 index 00000000000..e41919685a4 --- /dev/null +++ b/docs/notes/bugfix-21434.md @@ -0,0 +1 @@ +# Fix visual effects not working when acceleratedRendering is true on Android \ No newline at end of file diff --git a/docs/notes/bugfix-21443.md b/docs/notes/bugfix-21443.md new file mode 100644 index 00000000000..152fe0a6576 --- /dev/null +++ b/docs/notes/bugfix-21443.md @@ -0,0 +1 @@ +# Fix groups sometimes not redrawing when scrolled \ No newline at end of file diff --git a/docs/notes/bugfix-21468.md b/docs/notes/bugfix-21468.md new file mode 100644 index 00000000000..7e4c788931f --- /dev/null +++ b/docs/notes/bugfix-21468.md @@ -0,0 +1 @@ +# Include .framework folders in iOS extension code folders diff --git a/docs/notes/bugfix-21469.md b/docs/notes/bugfix-21469.md new file mode 100644 index 00000000000..c99048ca1b4 --- /dev/null +++ b/docs/notes/bugfix-21469.md @@ -0,0 +1 @@ +# Ensure iOS apps with embedded frameworks are correctly signed \ No newline at end of file diff --git a/docs/notes/bugfix-21488.md b/docs/notes/bugfix-21488.md new file mode 100644 index 00000000000..7a43ae2df67 --- /dev/null +++ b/docs/notes/bugfix-21488.md @@ -0,0 +1 @@ +# Fix automatic architecture detection on some Linux systems diff --git a/docs/notes/bugfix-21494.md b/docs/notes/bugfix-21494.md new file mode 100644 index 00000000000..d013ef1f4c1 --- /dev/null +++ b/docs/notes/bugfix-21494.md @@ -0,0 +1 @@ +# Correct missing text issue in the Round function dictionary entry. diff --git a/docs/notes/bugfix-21496.md b/docs/notes/bugfix-21496.md new file mode 100644 index 00000000000..89812e0345b --- /dev/null +++ b/docs/notes/bugfix-21496.md @@ -0,0 +1 @@ +# Ensure emscripten aux stacks are loaded on startup diff --git a/docs/notes/bugfix-21500.md b/docs/notes/bugfix-21500.md new file mode 100644 index 00000000000..ba3551da1f9 --- /dev/null +++ b/docs/notes/bugfix-21500.md @@ -0,0 +1 @@ +# Fix crash when ungrouping a group before calling quit diff --git a/docs/notes/bugfix-21527.md b/docs/notes/bugfix-21527.md new file mode 100644 index 00000000000..f11ed912568 --- /dev/null +++ b/docs/notes/bugfix-21527.md @@ -0,0 +1 @@ +# Include iPadPro splash image in plist file diff --git a/docs/notes/feature-android_runtime_permissions.md b/docs/notes/feature-android_runtime_permissions.md new file mode 100644 index 00000000000..85d603f5ca8 --- /dev/null +++ b/docs/notes/feature-android_runtime_permissions.md @@ -0,0 +1,26 @@ +# Android 6.0 runtime permissions + +Android 6.0 (API 23) Marshmallow introduced a new permissions model +that lets apps request permissions from the user at runtime, rather +than prior to installation. Apps built with LC 9.0.1 do support this +new permissions model, and request permissions automatically when the +app actually requires the services or data protected by the services. + +For example, if the app calls `mobilePickPhoto "camera"`, a dialog will +be shown to the user asking for permission to access the device camera. + +If the user does not grant permission, the call will fail. Moreover, the +app can use the function `androidRequestPermission(permissionName)` to +check if the permission for `permissionName` has been granted. + +Notes: + +- You have to make sure that you check the required permissions for your +app in the standalone settings. +- Apps that run on devices running Android 6+ will work with the new +permissions model. +- Apps that run on older devices (less than Android 6) will continue to +work with the old permissions model. +- If the user does not grant a permission when the dialog appears for the +first time, they can change this preference from the Settings app. + diff --git a/engine/rsrc/emscripten-startup-template.livecodescript b/engine/rsrc/emscripten-startup-template.livecodescript index 3638d194e57..337612d6959 100644 --- a/engine/rsrc/emscripten-startup-template.livecodescript +++ b/engine/rsrc/emscripten-startup-template.livecodescript @@ -1,4 +1,3 @@ -script "__startup" constant kEngineVersion = "@ENGINE_VERSION@" -- Directories that engine expects to normally be present @@ -7,41 +6,38 @@ constant kStandardFolders = "/tmp:/livecode:/boot:/boot/standalone:/boot/fonts" -- Directory containing the initial stack files constant kStartupFolder = "/boot/standalone" -on startup - local tError, tFolder - try - ---------------------------------------------------------------- - -- Create standard filesystem layout - set the itemdelimiter to ":" - repeat for each item tFolder in kStandardFolders - if there is not a folder tFolder then - create folder tFolder - if the result is not empty then - throw the result - end if +local tError, tFolder +try + ---------------------------------------------------------------- + -- Create standard filesystem layout + set the itemdelimiter to ":" + repeat for each item tFolder in kStandardFolders + if there is not a folder tFolder then + create folder tFolder + if the result is not empty then + throw the result end if - end repeat - - ------------------------------------------------------------- - -- Validate engine version - if the version is not kEngineVersion then - throw "Engine mismatch: found" && the version & ", expected" && kEngineVersion end if - - - @STARTUP_SCRIPT@ - - catch tError - end try - - -- Set the initial working directory to the directory that contains - -- the initial stack. - set the defaultfolder to kStartupFolder - - -- Try to print something vaguely helpful to the the log - if tError is not empty then - write "startup failed:" && tError & return to stderr + end repeat + + ------------------------------------------------------------- + -- Validate engine version + if the version is not kEngineVersion then + throw "Engine mismatch: found" && the version & ", expected" && kEngineVersion end if - - return tError -end startup + + + @STARTUP_SCRIPT@ + +catch tError +end try + +-- Try to print something vaguely helpful to the the log +if tError is not empty then + write "startup failed:" && tError & return to stderr + throw tError +end if + +-- Set the initial working directory to the directory that contains +-- the initial stack. +set the defaultfolder to kStartupFolder diff --git a/engine/src/cgimageutil.cpp b/engine/src/cgimageutil.cpp index fc09c878f08..573343b62d0 100644 --- a/engine/src/cgimageutil.cpp +++ b/engine/src/cgimageutil.cpp @@ -114,7 +114,7 @@ bool MCGRasterCreateCGDataProvider(const MCGRaster &p_raster, const MCGIntegerRe t_width = p_src_rect.size.width; t_height = p_src_rect.size.height; - const uint8_t *t_src_ptr = (uint8_t*)MCGRasterGetPixelPtr(p_raster, t_x, t_y); + uint8_t *t_src_ptr = (uint8_t*)MCGRasterGetPixelPtr(p_raster, t_x, t_y); uint32_t t_dst_stride; @@ -125,8 +125,10 @@ bool MCGRasterCreateCGDataProvider(const MCGRaster &p_raster, const MCGIntegerRe if (!p_copy) { t_dst_stride = p_raster.stride; - t_data_provider = CGDataProviderCreateWithData(nil, t_src_ptr, t_height * p_raster.stride, nil); + t_data_provider = CGDataProviderCreateWithData(nil, t_src_ptr, t_height * p_raster.stride, __CGDataProviderDeallocate); t_success = t_data_provider != nil; + if (!t_success) + MCMemoryDeallocate(t_src_ptr); } else { diff --git a/engine/src/exec-misc.cpp b/engine/src/exec-misc.cpp index f5bc5eebf22..432396ca6e0 100644 --- a/engine/src/exec-misc.cpp +++ b/engine/src/exec-misc.cpp @@ -481,6 +481,31 @@ void MCMiscGetDoNotBackupFile(MCExecContext& ctxt, MCStringRef p_path, bool& r_n ctxt.Throw(); } +void MCMiscExecRequestPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_granted) +{ + if (MCSystemRequestPermission(p_permission, r_granted)) + return; + + ctxt.Throw(); +} + +void MCMiscExecPermissionExists(MCExecContext& ctxt, MCStringRef p_permission, bool& r_exists) +{ + if (MCSystemPermissionExists(p_permission, r_exists)) + return; + + ctxt.Throw(); +} + +void MCMiscExecHasPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_permission_granted) +{ + if (MCSystemHasPermission(p_permission, r_permission_granted)) + return; + + ctxt.Throw(); +} + + void MCMiscSetDoNotBackupFile(MCExecContext& ctxt, MCStringRef p_path, bool p_no_backup) { if (MCSystemFileSetDoNotBackup(p_path, p_no_backup)) diff --git a/engine/src/exec.h b/engine/src/exec.h index 81f22a8fe02..2644f12c558 100644 --- a/engine/src/exec.h +++ b/engine/src/exec.h @@ -4043,6 +4043,9 @@ void MCMiscExecLibUrlDownloadToFile(MCExecContext& ctxt, MCStringRef p_url, MCSt void MCMiscExecLibUrlSetSSLVerification(MCExecContext& ctxt, bool p_enabled); void MCMiscGetBuildInfo(MCExecContext& ctxt, MCStringRef p_key, MCStringRef& r_value); +void MCMiscExecRequestPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_granted); +void MCMiscExecPermissionExists(MCExecContext& ctxt, MCStringRef p_permission, bool& r_exists); +void MCMiscExecHasPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_permission_granted); void MCMiscExecEnableRemoteControl(MCExecContext& ctxt); void MCMiscExecDisableRemoteControl(MCExecContext& ctxt); diff --git a/engine/src/executionerrors.h b/engine/src/executionerrors.h index 90b4e821948..7a4f1dd565c 100644 --- a/engine/src/executionerrors.h +++ b/engine/src/executionerrors.h @@ -2774,6 +2774,9 @@ enum Exec_errors // {EE-0908} fontLanguage: bad font name EE_FONTLANGUAGE_BADFONTNAME, + // {EE-0909} android permission: bad permission name + EE_BAD_PERMISSION_NAME, + }; extern const char *MCexecutionerrors; diff --git a/engine/src/globals.cpp b/engine/src/globals.cpp index d2bad3c3286..809ed0a7672 100644 --- a/engine/src/globals.cpp +++ b/engine/src/globals.cpp @@ -1342,6 +1342,7 @@ int X_close(void) while (MCsavegroupptr != NULL) { MCControl *gptr = MCsavegroupptr->remove(MCsavegroupptr); + gptr -> removereferences(); delete gptr; } diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index bc22d9fe676..fe40eb081ab 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -502,12 +502,12 @@ else if (key.getAction() == KeyEvent.ACTION_MULTIPLE) // IM-2013-02-21: [[ BZ 10684 ]] // allow BaseInputConnection to do the handling of commitText(), etc // and instead catch the raw key events that are generated. - if (key.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) + if (t_key_code == KeyEvent.KEYCODE_UNKNOWN) { // handle string of chars CharSequence t_chars = key.getCharacters(); for (int i = 0; i < t_chars.length(); i++) - doKeyPress(0, t_chars.charAt(i), 0); + handleKey(t_key_code, t_chars.charAt(i)); } else { @@ -548,10 +548,10 @@ void updateComposingText(CharSequence p_new) // send backspaces for (int i = 0; i < t_current_length - t_match_length; i++) - doKeyPress(0, 0, 0xff08); + handleKey(KeyEvent.KEYCODE_DEL, 0); // send new text for (int i = t_match_length; i < t_new_length; i++) - doKeyPress(0, p_new.charAt(i), 0); + handleKey(KeyEvent.KEYCODE_UNKNOWN, p_new.charAt(i)); m_composing_text = p_new; @@ -1151,6 +1151,11 @@ public void onListPickerDone(int p_index, boolean p_done) if (m_wake_on_event) doProcess(false); } + + public void onAskPermissionDone(boolean p_granted) + { + doAskPermissionDone(p_granted); + } //////////////////////////////////////////////////////////////////////////////// @@ -1859,23 +1864,121 @@ else if (t_caminfo.facing == CameraCompat.CameraInfo.CAMERA_FACING_FRONT) return new String(t_directions); } - public void showPhotoPicker(String p_source, int p_width, int p_height) - { - m_photo_width = p_width; - m_photo_height = p_height; - - if (p_source.equals("camera")) - showCamera(); - else if (p_source.equals("album")) - showLibrary(); - else if (p_source.equals("library")) - showLibrary(); - else - { - doPhotoPickerError("source not available"); - } - } - + public static final int PERMISSION_REQUEST_CODE = 1; + public boolean askPermission(String p_permission) + { + if (Build.VERSION.SDK_INT >= 23 && getContext().checkSelfPermission(p_permission) + != PackageManager.PERMISSION_GRANTED) + { + Activity t_activity = (LiveCodeActivity)getContext(); + t_activity.requestPermissions(new String[]{p_permission}, PERMISSION_REQUEST_CODE); + } + else + onAskPermissionDone(true); + return true; + } + + public boolean checkHasPermissionGranted(String p_permission) + { + if (Build.VERSION.SDK_INT >= 23) + { + return getContext().checkSelfPermission(p_permission) == PackageManager.PERMISSION_GRANTED; + } + return true; + } + + + public boolean checkPermissionExists(String p_permission) + { + if (Build.VERSION.SDK_INT >= 23) + { + List t_group_info_list = getAllPermissionGroups(); + if (t_group_info_list == null) + return false; + + ArrayList t_group_name_list = new ArrayList(); + for (PermissionGroupInfo t_group_info : t_group_info_list) + { + String t_group_name = t_group_info.name; + if (t_group_name != null) + t_group_name_list.add(t_group_name); + } + + for (String t_group_name : t_group_name_list) + { + ArrayList t_permission_name_list = getPermissionsForGroup(t_group_name); + + if (t_permission_name_list.contains(p_permission)) + return true; + } + return false; + } + return true; + } + + private List getAllPermissionGroups() + { + final PackageManager t_package_manager = getContext().getPackageManager(); + if (t_package_manager == null) + return null; + + return t_package_manager.getAllPermissionGroups(0); + } + + private ArrayList getPermissionsForGroup(String p_group_name) + { + final PackageManager t_package_manager = getContext().getPackageManager(); + final ArrayList t_permission_name_list = new ArrayList(); + + try + { + List t_permission_info_list = + t_package_manager.queryPermissionsByGroup(p_group_name, PackageManager.GET_META_DATA); + if (t_permission_info_list != null) + { + for (PermissionInfo t_permission_info : t_permission_info_list) + { + String t_permission_name = t_permission_info.name; + t_permission_name_list.add(t_permission_name); + } + } + } + catch (PackageManager.NameNotFoundException e) + { + // e.printStackTrace(); + Log.d(TAG, "permissions not found for group = " + p_group_name); + } + + Collections.sort(t_permission_name_list); + + return t_permission_name_list; + } + + + public void showPhotoPicker(String p_source, int p_width, int p_height) + { + m_photo_width = p_width; + m_photo_height = p_height; + + if (p_source.equals("camera")) + showCamera(); + else if (p_source.equals("album")) + showLibrary(); + else if (p_source.equals("library")) + showLibrary(); + else + { + doPhotoPickerError("source not available"); + } + + } + + // sent by the callback + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + onAskPermissionDone(grantResults[0] == PackageManager.PERMISSION_GRANTED); + } + public void showCamera() { // 2012-01-18-IM temp file may be created in app cache folder, in which case @@ -2251,7 +2354,8 @@ public void run() { public void showBitmapView() { - ensureBitmapViewVisibility(); + // force visible for visual effects + m_bitmap_view.setVisibility(View.VISIBLE); } //////////////////////////////////////////////////////////////////////////////// @@ -3736,6 +3840,7 @@ public static native void doHeadingChanged(double p_heading, double p_magnetic_h public static native void doDatePickerDone(int year, int month, int day, boolean done); public static native void doTimePickerDone(int hour, int minute, boolean done); public static native void doListPickerDone(int index, boolean done); + public static native void doAskPermissionDone(boolean granted); public static native void doMovieStopped(); public static native void doMovieTouched(); diff --git a/engine/src/java/com/runrev/android/LiveCodeActivity.java b/engine/src/java/com/runrev/android/LiveCodeActivity.java index f0ba460584a..49279915e72 100644 --- a/engine/src/java/com/runrev/android/LiveCodeActivity.java +++ b/engine/src/java/com/runrev/android/LiveCodeActivity.java @@ -23,6 +23,7 @@ import android.content.res.*; import android.widget.*; import android.util.*; +import android.content.pm.PackageManager; // This is the main activity exported by the application. This is // split into two parts, a customizable sub-class that gets dynamically @@ -198,4 +199,13 @@ protected void onActivityResult (int requestCode, int resultCode, Intent data) { s_main_view.onActivityResult(requestCode, resultCode, data); } + + // Callback sent when the app requests permissions on runtime (Android API 23+) + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + s_main_view.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } diff --git a/engine/src/java/com/runrev/android/SensorModule.java b/engine/src/java/com/runrev/android/SensorModule.java index 29fabd62746..58b0f77a68c 100644 --- a/engine/src/java/com/runrev/android/SensorModule.java +++ b/engine/src/java/com/runrev/android/SensorModule.java @@ -19,7 +19,6 @@ import android.content.*; import android.os.*; import android.util.*; - import java.util.*; import android.hardware.*; diff --git a/engine/src/mac-abort.mm b/engine/src/mac-abort.mm index 2eb33542a79..9f22719ec26 100644 --- a/engine/src/mac-abort.mm +++ b/engine/src/mac-abort.mm @@ -151,6 +151,58 @@ static CGEventRef abort_key_callback(CGEventTapProxy p_proxy, CGEventType p_type } #endif +static void update_keyboard_input_source() +{ + // Update our period key mapping if the input source has changed / hasn't + // been initialized. + TISInputSourceRef t_input_source; + t_input_source = TISCopyCurrentKeyboardInputSource(); + if (t_input_source != s_current_input_source || s_current_input_source == nil) + { + if (s_current_input_source != nil) + CFRelease(s_current_input_source); + + s_current_input_source = t_input_source; + s_current_period_keycode = 0xffff; + s_current_period_needs_shift = false; + + // If we have a valid keyboard input source then resolve '.'. + if (s_current_input_source != nil) + { + // Loop through all possible keycodes and map with no-shift and shift + // to see if we can find our '.' key. + for(uindex_t i = 0; i < 127; i++) + { + unichar t_char; + t_char = map_keycode_to_char(t_input_source, i, false); + if (t_char == '.') + { + s_current_period_keycode = i; + s_current_period_needs_shift = false; + break; + } + + t_char = map_keycode_to_char(t_input_source, i, true); + if (t_char == '.') + { + s_current_period_keycode = i; + s_current_period_needs_shift = true; + break; + } + } + } + } +} + +void update_keyboard_input_source_callback(CFNotificationCenterRef p_center, + void *p_observer, + CFStringRef p_name, + const void *p_object, + CFDictionaryRef p_userInfo) +{ + update_keyboard_input_source(); +} + static void abort_key_timer_callback(CFRunLoopTimerRef p_timer, void *p_info) { if (s_abort_key_disabled > 0) @@ -159,46 +211,6 @@ static void abort_key_timer_callback(CFRunLoopTimerRef p_timer, void *p_info) } s_abort_key_checked = false; - - // Update our period key mapping if the input source has changed / hasn't - // been initialized. - TISInputSourceRef t_input_source; - t_input_source = TISCopyCurrentKeyboardInputSource(); - if (t_input_source != s_current_input_source || s_current_input_source == nil) - { - if (s_current_input_source != nil) - CFRelease(s_current_input_source); - - s_current_input_source = t_input_source; - s_current_period_keycode = 0xffff; - s_current_period_needs_shift = false; - - // If we have a valid keyboard input source then resolve '.'. - if (s_current_input_source != nil) - { - // Loop through all possible keycodes and map with no-shift and shift - // to see if we can find our '.' key. - for(uindex_t i = 0; i < 127; i++) - { - unichar t_char; - t_char = map_keycode_to_char(t_input_source, i, false); - if (t_char == '.') - { - s_current_period_keycode = i; - s_current_period_needs_shift = false; - break; - } - - t_char = map_keycode_to_char(t_input_source, i, true); - if (t_char == '.') - { - s_current_period_keycode = i; - s_current_period_needs_shift = true; - break; - } - } - } - } // If we successfully found period - we must now check the state of the keys. if (s_current_period_keycode != 0xffff) @@ -343,6 +355,15 @@ bool MCPlatformInitializeAbortKey(void) if (!AXAPIEnabled()) return true; #endif + + update_keyboard_input_source(); + + CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), + nullptr, + update_keyboard_input_source_callback, + kTISNotifySelectedKeyboardInputSourceChanged, + nullptr, + CFNotificationSuspensionBehaviorDeliverImmediately); s_abort_key_thread = [[MCAbortKeyThread alloc] init]; [s_abort_key_thread start]; @@ -351,6 +372,11 @@ bool MCPlatformInitializeAbortKey(void) void MCPlatformFinalizeAbortKey(void) { + CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), + nullptr, + kTISNotifySelectedKeyboardInputSourceChanged, + nullptr); + [s_abort_key_thread terminate]; [s_abort_key_thread release]; s_abort_key_thread = nil; diff --git a/engine/src/mac-cursor.mm b/engine/src/mac-cursor.mm index 31d2aeae782..135c6d5007f 100644 --- a/engine/src/mac-cursor.mm +++ b/engine/src/mac-cursor.mm @@ -137,7 +137,7 @@ void MCPlatformCreateCustomCursor(MCImageBitmap *p_image, MCPoint p_hotspot, MCP t_cursor -> is_standard = false; CGImageRef t_cg_image; - /* UNCHECKED */ MCImageBitmapToCGImage(p_image, false, false, t_cg_image); + /* UNCHECKED */ MCImageBitmapToCGImage(p_image, true, false, t_cg_image); // Convert the CGImage into an NSIMage NSImage *t_cursor_image; diff --git a/engine/src/mac-surface.mm b/engine/src/mac-surface.mm index cccbaa9149c..559c0b58ddb 100644 --- a/engine/src/mac-surface.mm +++ b/engine/src/mac-surface.mm @@ -317,7 +317,6 @@ CGRect MCMacFlipCGRect(const CGRect &p_rect, uint32_t p_surface_height) // IM-2014-10-03: [[ Bug 13432 ]] Render with copy blend mode to replace destination alpha with the source alpha. MCMacRenderRasterToCG(m_cg_context, t_dst_rect, m_raster, MCGRectangleMake(0, 0, m_raster.width, m_raster.height), 1.0, kMCGBlendModeCopy); - free(m_raster . pixels); m_raster . pixels = nil; } diff --git a/engine/src/mblandroidcamera.cpp b/engine/src/mblandroidcamera.cpp index 8b1e8421b6e..74a36c0b76b 100644 --- a/engine/src/mblandroidcamera.cpp +++ b/engine/src/mblandroidcamera.cpp @@ -80,9 +80,13 @@ MCCamerasFeaturesType MCSystemGetAllCameraFeatures() return t_features; } +extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); bool MCAndroidPickPhoto(const char *p_source, int32_t p_max_width, int32_t p_max_height) { - MCAndroidEngineCall("showPhotoPicker", "vsii", nil, p_source, p_max_width, p_max_height); + if (!MCAndroidCheckRuntimePermission(MCSTR("android.permission.CAMERA"))) + return false; + + MCAndroidEngineCall("showPhotoPicker", "vsii", nil, p_source, p_max_width, p_max_height); // SN-2014-09-03: [[ Bug 13329 ]] MCAndroidPickPhoto's return value is ignored in 6.x, // but not in 7.0 - whence the failure in mobilePickPhoto return true; diff --git a/engine/src/mblandroidcontact.cpp b/engine/src/mblandroidcontact.cpp index cae48765ea6..70eb74cd9a9 100644 --- a/engine/src/mblandroidcontact.cpp +++ b/engine/src/mblandroidcontact.cpp @@ -58,9 +58,11 @@ static MCAndroidContactStatus s_contact_status = kMCAndroidContactWaiting; static int32_t s_contact_selected = 0; static MCString s_contacts_selected = ""; +extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); bool MCSystemPickContact(int32_t& r_result) { MCLog("MCSystemPickContact"); + MCAndroidEngineRemoteCall("pickContact", "i", &r_result); s_contact_status = kMCAndroidContactWaiting; while (s_contact_status == kMCAndroidContactWaiting) @@ -156,6 +158,9 @@ bool MCSystemUpdateContact(MCArrayRef p_contact, MCStringRef p_title, MCStringRef p_message, MCStringRef p_alternate_name, int32_t &r_result) { + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_CONTACTS")))) + return false; + MCLog("MCSystemUpdateContact"); bool t_success = true; @@ -192,6 +197,10 @@ void MCAndroidUpdateContactCanceled(int32_t p_contact_id) bool MCSystemGetContactData(int32_t p_contact_id, MCArrayRef &r_contact_data) { MCLog("MCSystemGetContactData: %d", p_contact_id); + + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.READ_CONTACTS")))) + return false; + jobject t_jmap = nil; MCAndroidEngineRemoteCall("getContactData", "mi", &t_jmap, p_contact_id); MCLog("contact map: %p", t_jmap); @@ -209,6 +218,10 @@ bool MCSystemGetContactData(int32_t p_contact_id, MCArrayRef &r_contact_data) bool MCSystemRemoveContact(int32_t p_contact_id) { MCLog("MCSystemRemoveContact: %d", p_contact_id); + + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_CONTACTS")))) + return false; + MCAndroidEngineRemoteCall("removeContact", "vi", nil, p_contact_id); return true; } @@ -216,6 +229,9 @@ bool MCSystemRemoveContact(int32_t p_contact_id) bool MCSystemAddContact(MCArrayRef p_contact, int32_t &r_result) { MCLog("MCSystemAddContact"); + + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_CONTACTS")))) + return false; bool t_success = true; jobject t_map = nil; @@ -229,8 +245,12 @@ bool MCSystemAddContact(MCArrayRef p_contact, int32_t &r_result) return false; } + bool MCSystemFindContact(MCStringRef p_contact_name, MCStringRef& r_result) { + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.READ_CONTACTS")))) + return false; + MCAndroidEngineRemoteCall("findContact", "vx", nil, p_contact_name); return MCStringCreateWithCString(s_contacts_selected . getstring(), r_result); } diff --git a/engine/src/mblandroiddc.cpp b/engine/src/mblandroiddc.cpp index 4ef4f42b4d9..7d8801ae5f9 100644 --- a/engine/src/mblandroiddc.cpp +++ b/engine/src/mblandroiddc.cpp @@ -2817,6 +2817,46 @@ void MCAndroidDisableOpenGLMode(void) //////////////////////////////////////////////////////////////////////////////// +static bool s_in_permission_dialog = false; +static bool s_permission_granted = false; +bool MCAndroidCheckRuntimePermission(MCStringRef p_permission) +{ + bool t_result; + s_in_permission_dialog = true; + MCAndroidEngineRemoteCall("askPermission", "bx", &t_result, p_permission); + + while (s_in_permission_dialog) + MCscreen -> wait(60.0, False, True); + + return s_permission_granted; +} + +bool MCAndroidCheckPermissionExists(MCStringRef p_permission) +{ + bool t_result; + MCAndroidEngineRemoteCall("checkPermissionExists", "bx", &t_result, p_permission); + + return t_result; +} + +bool MCAndroidHasPermission(MCStringRef p_permission) +{ + bool t_result; + MCAndroidEngineRemoteCall("checkHasPermissionGranted", "bx", &t_result, p_permission); + + return t_result; +} + +extern "C" JNIEXPORT void JNICALL Java_com_runrev_android_Engine_doAskPermissionDone(JNIEnv *env, jobject object, bool granted) __attribute__((visibility("default"))); +JNIEXPORT void JNICALL Java_com_runrev_android_Engine_doAskPermissionDone(JNIEnv *env, jobject object, bool granted) +{ + s_in_permission_dialog = false; + s_permission_granted = granted; + MCAndroidBreakWait(); +} + +///////////////////////////////////////////////////////////////////////////////// + bool android_run_on_main_thread(void *p_callback, void *p_callback_state, int p_options); typedef void (*MCExternalThreadOptionalCallback)(void *state); diff --git a/engine/src/mblandroidfs.cpp b/engine/src/mblandroidfs.cpp index 57f3ca7e306..1b18e3ffb90 100644 --- a/engine/src/mblandroidfs.cpp +++ b/engine/src/mblandroidfs.cpp @@ -415,23 +415,32 @@ bool MCAndroidSystem::GetTemporaryFileName(MCStringRef &r_tmp_name) return MCStringCreateWithCString(tmpnam(NULL), r_tmp_name); } +extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); Boolean MCAndroidSystem::GetStandardFolder(MCNameRef p_folder, MCStringRef &r_folder) { + // accessing "external documents", "external cache" etc requires Write External Storage permission + if (MCStringBeginsWith(MCNameGetString(p_folder), MCSTR("external"), kMCStringOptionCompareCaseless) && \ + !MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_EXTERNAL_STORAGE"))) + { + r_folder = MCValueRetain(kMCEmptyString); + return False; + } + // SN-2015-04-16: [[ Bug 14295 ]] The resources folder on Mobile is the same // as the engine folder. if (MCNameIsEqualToCaseless(p_folder, MCN_engine) - || MCNameIsEqualToCaseless(p_folder, MCN_resources)) + || MCNameIsEqualToCaseless(p_folder, MCN_resources)) { MCLog("GetStandardFolder(\"%@\") -> \"%@\"", MCNameGetString(p_folder), MCcmd); - return MCStringCopy(MCcmd, r_folder); + return MCStringCopy(MCcmd, r_folder); } - MCAutoStringRef t_stdfolder; + MCAutoStringRef t_stdfolder; MCAndroidEngineCall("getSpecialFolderPath", "xx", &(&t_stdfolder), MCNameGetString(p_folder)); MCLog("GetStandardFolder(\"%@\") -> \"%@\"", p_folder, *t_stdfolder == nil ? kMCEmptyString : *t_stdfolder); - r_folder = MCValueRetain(*t_stdfolder == nil ? kMCEmptyString : *t_stdfolder); + r_folder = MCValueRetain(*t_stdfolder == nil ? kMCEmptyString : *t_stdfolder); return True; } diff --git a/engine/src/mblandroidmisc.cpp b/engine/src/mblandroidmisc.cpp index 5bc82aef127..477ea6fa94b 100644 --- a/engine/src/mblandroidmisc.cpp +++ b/engine/src/mblandroidmisc.cpp @@ -802,6 +802,26 @@ bool MCSystemBuildInfo(MCStringRef p_key, MCStringRef& r_value) //////////////////////////////////////////////////////////////////////////////// +bool MCSystemRequestPermission(MCStringRef p_permission, bool& r_granted) +{ + r_granted = MCAndroidCheckRuntimePermission(p_permission); + return true; +} + +bool MCSystemPermissionExists(MCStringRef p_permission, bool& r_exists) +{ + r_exists = MCAndroidCheckPermissionExists(p_permission); + return true; +} + +bool MCSystemHasPermission(MCStringRef p_permission, bool& r_permission_granted) +{ + r_permission_granted = MCAndroidHasPermission(p_permission); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// diff --git a/engine/src/mblandroidsensor.cpp b/engine/src/mblandroidsensor.cpp index 4851f473e2d..3f1182c000b 100644 --- a/engine/src/mblandroidsensor.cpp +++ b/engine/src/mblandroidsensor.cpp @@ -58,9 +58,18 @@ void MCSystemSensorFinalize(void) } //////////////////////////////////////////////////////////////////////////////// +extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); bool MCSystemGetSensorAvailable(MCSensorType p_sensor, bool& r_available) -{ +{ + if (p_sensor == kMCSensorTypeLocation) + { + bool t_success = MCAndroidCheckRuntimePermission(MCSTR("android.permission.ACCESS_COARSE_LOCATION")) && \ + MCAndroidCheckRuntimePermission(MCSTR("android.permission.ACCESS_FINE_LOCATION")); + if (!t_success) + return false; + } + MCAndroidEngineRemoteCall("isSensorAvailable", "bi", &r_available, (int32_t)p_sensor); return true; } diff --git a/engine/src/mblandroidutil.h b/engine/src/mblandroidutil.h index 42fd778739e..108877c0a7f 100644 --- a/engine/src/mblandroidutil.h +++ b/engine/src/mblandroidutil.h @@ -63,6 +63,9 @@ void MCAndroidObjectCall(jobject p_object, const char *p_method, const char *p_s void MCAndroidObjectRemoteCall(jobject p_object, const char *p_method, const char *p_signature, void *p_return_value, ...); bool MCAndroidGetBuildInfo(MCStringRef t_key, MCStringRef &r_value); +bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); +bool MCAndroidCheckPermissionExists(MCStringRef p_permission); +bool MCAndroidHasPermission(MCStringRef p_permission); typedef struct _android_device_configuration { diff --git a/engine/src/mblhandlers.cpp b/engine/src/mblhandlers.cpp index ab260dc5817..2a5f0b48422 100644 --- a/engine/src/mblhandlers.cpp +++ b/engine/src/mblhandlers.cpp @@ -3377,6 +3377,99 @@ Exec_stat MCHandleBuildInfo(void *context, MCParameter *p_parameters) return ES_ERROR; } +/////////////////// Android 6.0 runtime permissions ///////////////////////// +Exec_stat MCHandleRequestPermission(void *context, MCParameter *p_parameters) +{ + MCExecContext ctxt(nil, nil, nil); + + MCAutoStringRef t_permission; + bool t_success, t_granted; + + t_success = MCParseParameters(p_parameters, "x", &(&t_permission)); + + bool t_permission_exists; + MCMiscExecPermissionExists(ctxt, *t_permission, t_permission_exists); + + if (!t_permission_exists) + { + ctxt.LegacyThrow(EE_BAD_PERMISSION_NAME); + t_success = false; + } + + if (t_success) + MCMiscExecRequestPermission(ctxt, *t_permission, t_granted); + + Exec_stat t_stat; + if (!ctxt . HasError()) + t_stat = ES_NORMAL; + else + t_stat = ES_ERROR; + + ctxt.SetTheResultToEmpty(); + return t_stat; +} + +Exec_stat MCHandlePermissionExists(void *context, MCParameter *p_parameters) +{ + MCExecContext ctxt(nil, nil, nil); + + MCAutoStringRef t_permission; + bool t_success, t_exists; + + t_success = MCParseParameters(p_parameters, "x", &(&t_permission)); + + if (t_success) + MCMiscExecPermissionExists(ctxt, *t_permission, t_exists); + + if (!ctxt . HasError()) + { + if (t_exists) + ctxt.SetTheResultToValue(kMCTrueString); + else + ctxt.SetTheResultToValue(kMCFalseString); + + return ES_NORMAL; + } + + ctxt.SetTheResultToEmpty(); + return ES_ERROR; +} + +Exec_stat MCHandleHasPermission(void *context, MCParameter *p_parameters) +{ + MCExecContext ctxt(nil, nil, nil); + + MCAutoStringRef t_permission; + bool t_success, t_permission_granted; + + t_success = MCParseParameters(p_parameters, "x", &(&t_permission)); + + bool t_permission_exists; + MCMiscExecPermissionExists(ctxt, *t_permission, t_permission_exists); + + if (!t_permission_exists) + { + ctxt.LegacyThrow(EE_BAD_PERMISSION_NAME); + t_success = false; + } + + if (t_success) + MCMiscExecHasPermission(ctxt, *t_permission, t_permission_granted); + + if (!ctxt . HasError()) + { + if (t_permission_granted) + ctxt.SetTheResultToValue(kMCTrueString); + else + ctxt.SetTheResultToValue(kMCFalseString); + + return ES_NORMAL; + } + + ctxt.SetTheResultToEmpty(); + return ES_ERROR; +} + ////////////////////////////////////////////////////////////////////////////////////// static MCMediaType MCMediaTypeFromString(MCStringRef p_string) @@ -4529,6 +4622,9 @@ static const MCPlatformMessageSpec s_platform_messages[] = {false, "mobileLocationAuthorizationStatus", MCHandleLocationAuthorizationStatus, nil}, {false, "mobileBuildInfo", MCHandleBuildInfo, nil}, + {false, "androidRequestPermission", MCHandleRequestPermission, nil}, + {false, "androidPermissionExists", MCHandlePermissionExists, nil}, + {false, "androidHasPermission", MCHandleHasPermission, nil}, {false, "mobileCanMakePurchase", MCHandleCanMakePurchase, nil}, {false, "mobileEnablePurchaseUpdates", MCHandleEnablePurchaseUpdates, nil}, diff --git a/engine/src/mbliphonegfx.mm b/engine/src/mbliphonegfx.mm index 22ac7370a2a..a75861d9f02 100644 --- a/engine/src/mbliphonegfx.mm +++ b/engine/src/mbliphonegfx.mm @@ -102,7 +102,7 @@ static void do_update(void *p_dirty) MCGRegionRef m_region; bool m_own_region; - virtual void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) = 0; + virtual void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride, bool &x_taken) = 0; public: MCIPhoneStackSurface(MCGRegionRef p_region) @@ -197,10 +197,16 @@ void UnlockPixels(MCGIntegerRectangle p_area, MCGRaster& p_raster, bool p_update if (p_raster . pixels == nil) return; + bool t_taken = false; + if (p_update) - FlushBits(p_area, p_raster . pixels, p_raster . stride); + FlushBits(p_area, p_raster . pixels, p_raster . stride, t_taken); + + if (!t_taken) + { + free(p_raster.pixels); + } - free(p_raster . pixels); } bool Composite(MCGRectangle p_dst_rect, MCGImageRef p_src, MCGRectangle p_src_rect, MCGFloat p_alpha, MCGBlendMode p_blend) @@ -337,7 +343,7 @@ void UnlockTarget(void) protected: // MM-2014-07-31: [[ ThreadedRendering ]] Updated to pass in the area we wish to draw. - void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) + void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride, bool &x_taken) { void *t_target; if (!LockTarget(kMCStackSurfaceTargetCoreGraphics, t_target)) @@ -368,6 +374,8 @@ void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) if (MCGRasterToCGImage(t_raster, MCGIntegerRectangleMake(0, 0, p_area.size.width, p_area.size.height), t_colorspace, false, false, t_image)) { + x_taken = true; + CGContextDrawImage(t_context, CGRectMake((float)p_area.origin.x, (float)(m_height - (p_area.origin.y + p_area.size.height)), (float)p_area.size.width, (float)p_area.size.height), t_image); CGImageRelease(t_image); } @@ -521,7 +529,7 @@ void UnlockTarget(void) protected: // MM-2014-07-31: [[ ThreadedRendering ]] Updated to pass in the area we wish to draw. - void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) + void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride, bool& x_taken) { GLuint t_texture; glGenTextures(1, &t_texture); @@ -582,7 +590,7 @@ void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) } glDeleteTextures(1, &t_texture); - } + } }; @implementation MCIPhoneOpenGLDisplayView diff --git a/engine/src/mbliphonemisc.mm b/engine/src/mbliphonemisc.mm index 3b497d95ac1..2fbcceb7fee 100644 --- a/engine/src/mbliphonemisc.mm +++ b/engine/src/mbliphonemisc.mm @@ -26,3 +26,25 @@ int32_t MCCustomPrinterComputeFontSize(void *font) { return CTFontGetSize((CTFontRef)font); } + +//////////////////////////////////////////////////////////////////////////////// + +bool MCSystemRequestPermission(MCStringRef p_permission, bool& r_granted) +{ + // Not implemented + return false; +} + +bool MCSystemPermissionExists(MCStringRef p_permission, bool& r_exists) +{ + // Not implemented + return false; +} + +bool MCSystemHasPermission(MCStringRef p_permission, bool& r_permission_granted) +{ + // Not implemented + return false; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/engine/src/mblsyntax.h b/engine/src/mblsyntax.h index 9ccfaa68249..7f653d92584 100644 --- a/engine/src/mblsyntax.h +++ b/engine/src/mblsyntax.h @@ -466,6 +466,9 @@ bool MCSystemFileSetDataProtection(MCStringRef p_path, MCStringRef p_protection_ bool MCSystemFileGetDataProtection(MCStringRef p_path, MCStringRef& r_protection_string); bool MCSystemBuildInfo(MCStringRef p_key, MCStringRef& r_value); +bool MCSystemRequestPermission(MCStringRef p_permission, bool& r_granted); +bool MCSystemPermissionExists(MCStringRef p_permission, bool& r_exists); +bool MCSystemHasPermission(MCStringRef p_permission, bool& r_permission_granted); bool MCSystemEnableRemoteControl(); bool MCSystemDisableRemoteControl(); diff --git a/engine/src/redraw.cpp b/engine/src/redraw.cpp index 9e09d160851..0fe196e8d79 100644 --- a/engine/src/redraw.cpp +++ b/engine/src/redraw.cpp @@ -599,14 +599,10 @@ void MCControl::layer_dirtyeffectiverect(const MCRectangle& p_effective_rect, bo MCRectangle32 t_device_rect; t_device_rect = MCRectangle32GetTransformedBounds(t_dirty_rect, t_transform); - // Notify any tilecache of the changes. - if (t_tilecache != nil) + // Notify any tilecache of the changes + // If the layer id is zero, there is nothing to do. + if (t_tilecache != nil && t_control -> m_layer_id != 0) { - // We must be in tile-cache mode with a top-level control, but if the layer - // id is zero, there is nothing to do. - if (t_control -> m_layer_id == 0) - return; - // How we handle the layer depends on whether it is a sprite or not. if (!t_control -> layer_issprite()) { diff --git a/extensions/widgets/treeview/notes/21503.md b/extensions/widgets/treeview/notes/21503.md new file mode 100644 index 00000000000..e333f747434 --- /dev/null +++ b/extensions/widgets/treeview/notes/21503.md @@ -0,0 +1 @@ +# [21503] Prevent accidental value removal on key addition \ No newline at end of file diff --git a/extensions/widgets/treeview/treeview.lcb b/extensions/widgets/treeview/treeview.lcb index 65f3d99b239..460f7548a3f 100644 --- a/extensions/widgets/treeview/treeview.lcb +++ b/extensions/widgets/treeview/treeview.lcb @@ -759,7 +759,7 @@ public handler OnMouseDown() returns nothing end if end handler -public handler OnMouseMove() returns nothing +public handler OnMouseMove() returns nothing if scrollDragging() then variable tScrollPositionRatio as Real scrollbarDrag(mViewHeight) @@ -1514,9 +1514,23 @@ private handler addKey(in pListElt as Integer, in pPath as List, in pLevel as In if tElement is an array then createNewKey(xArray[pPath[pLevel + 1]]) else + if tElement is not "" then + variable tPrompt as String + combine pPath with "][" + put "The array element at path [" & the result & "] is not empty. " & \ + "Would you like to replace it with an empty array or " & \ + "move it to the first element?" into tPrompt + execute script "answer \q" & tPrompt & \ + "\q with Cancel or Replace or Move; return it" + if the result is "Replace" then + put "" into tElement + else if the result is "Cancel" then + return + end if + end if variable tArray as Array put the empty array into tArray - put "" into tArray[1 formatted as string] + put tElement into tArray[1 formatted as string] put tArray into xArray[pPath[pLevel + 1]] put false into xList[pListElt]["leaf"] end if diff --git a/ide-support/revsaveasemscriptenstandalone.livecodescript b/ide-support/revsaveasemscriptenstandalone.livecodescript index 6cb19c822a6..5a723d13c72 100644 --- a/ide-support/revsaveasemscriptenstandalone.livecodescript +++ b/ide-support/revsaveasemscriptenstandalone.livecodescript @@ -202,7 +202,10 @@ private command storeDeployedStack pZip, pDeployPath, pBuildFolder, pMainStack, put tTempStackPath into pDeployParams["stackfile"] put tTempDeployPath into pDeployParams["output"] put empty into pDeployParams["engine"] - + + // Externals are not yet supported in emscripten builds + put empty into pDeployParams["externals"] + ---------- Perform standalone deployment logDebug "deploy", "Deploying standalone" @@ -253,13 +256,9 @@ private function getStartupScript pGeneratedStartupScript throw tTemplateFile & ":" && the result end if close file tTemplateFile - - -- Trim to the actual script - delete line 1 of tScript - + -- Make substitutions in the startup script replace "@ENGINE_VERSION@" with the version in tScript - replace "@MODULE_VERSION@" with extensionLCCompileVersion() in tScript replace "@STARTUP_SCRIPT@" with pGeneratedStartupScript in tScript return tScript end getStartupScript diff --git a/ide-support/revsaveasiosstandalone.livecodescript b/ide-support/revsaveasiosstandalone.livecodescript index 34178634262..b04d6517178 100644 --- a/ide-support/revsaveasiosstandalone.livecodescript +++ b/ide-support/revsaveasiosstandalone.livecodescript @@ -801,7 +801,8 @@ private command revSaveAsMobileStandaloneMain pStack, pAppBundle, pTarget, pSett --try -- Perform the codesigning of the main bundle local tResult - put shell("/usr/bin/codesign --verbose -f -s" && quote & tCertificate & quote && \ + __CodesignFrameworks pAppBundle, tCertificate, tEntitlementsFile + put shell("/usr/bin/codesign --deep --verbose -f -s" && quote & tCertificate & quote && \ "--entitlements" && quote & tEntitlementsFile & quote && \ quote & pAppBundle & quote) into tResult -- MM-2012-10-25: [[ Bug ]] try catch finally oddness meant that any errors here were being ignored. @@ -817,6 +818,23 @@ private command revSaveAsMobileStandaloneMain pStack, pAppBundle, pTarget, pSett end if end revSaveAsMobileStandaloneMain +private command __CodesignFrameworks pPath, pCertificate, pEntitlements + local tFrameworks + put folders(pPath) into tFrameworks + filter tFrameworks with "*.framework" + repeat for each line tFramework in tFrameworks + __CodesignFrameworks pPath & "/" & tFramework & "/Frameworks", pCertificate, pEntitlements + + local tResult + put shell("/usr/bin/codesign --deep --verbose -f -s" && quote & pCertificate & quote && \ + "--entitlements" && quote & pEntitlements & quote && \ + quote & pPath & "/" & tFramework & quote) into tResult + if not (tResult contains "signed bundle" or tResult contains "signed app bundle") then + throw "codesigning failed with" && tResult + end if + end repeat +end __CodesignFrameworks + command revSaveAsMobileDeployer pDeployAppBundle, pAppBundle, pAppName, pAppId, pAppExecutables create folder pDeployAppBundle create folder pDeployAppBundle & slash & "Contents" @@ -1689,9 +1707,11 @@ private command revCreateMobilePlist pSettings, pAppBundle, pTarget, pFonts, pPl if 2 is among the items of tDeviceFamily then if tIPadOrientations contains "Portrait" then put "Default-Portrait|Default-Portrait|{768, 1024}|Portrait" & return after tSplashScreens + put "Default-iPadProPortrait|Default-iPadProPortrait|{1024, 1366}|Portrait" & return after tSplashScreens end if if tIPadOrientations contains "Landscape" then put "Default-Landscape|Default-Landscape|{768, 1024}|Landscape" & return after tSplashScreens + put "Default-iPadProLandscape|Default-iPadProLandscape|{1024, 1366}|Landscape" & return after tSplashScreens end if end if diff --git a/ide-support/revsblibrary.livecodescript b/ide-support/revsblibrary.livecodescript index 19eec7b84ca..984cff984eb 100644 --- a/ide-support/revsblibrary.livecodescript +++ b/ide-support/revsblibrary.livecodescript @@ -2616,7 +2616,7 @@ private command __UpdateSettingsFromCodeFolder pPath, pCodeFolderName, pPlatform appendToStringList tFolderPath & slash & tLine, xSettings["extension_code_resources"] end repeat - if pPlatform is "mac" then + if pPlatform is among the words of "mac ios" then local tFolders put folders(tFolderPath) into tFolders filter tFolders without ".*" diff --git a/libfoundation/src/foundation-foreign.cpp b/libfoundation/src/foundation-foreign.cpp index 4900cea38a1..a42cc8c62c2 100644 --- a/libfoundation/src/foundation-foreign.cpp +++ b/libfoundation/src/foundation-foreign.cpp @@ -148,7 +148,7 @@ typedef float natural_float_t; #elif defined(__64_BIT__) typedef uint64_t natural_uint_t; typedef int64_t natural_sint_t; -typedef float natural_float_t; +typedef double natural_float_t; #else #error Bitness of target not defined #endif diff --git a/libscript/src/arithmetic.lcb b/libscript/src/arithmetic.lcb index a83fb3f863a..d3ebe541625 100644 --- a/libscript/src/arithmetic.lcb +++ b/libscript/src/arithmetic.lcb @@ -590,12 +590,13 @@ The result: parsed as a number, or nothing. Example: variable tString as String put "5.6" into tString - parse tString as Number -- the result is 5.6 + parse tString as number -- the result is 5.6 Example: variable tResult as optional String variable tNum as optional Number - parse "aaaa" as Number into tNum + + parse "aaaa" as number into tNum if tNum is nothing then put "unable to parse string" into tResult diff --git a/libscript/src/math.lcb b/libscript/src/math.lcb index deb3c787a7a..0774cffc2bd 100644 --- a/libscript/src/math.lcb +++ b/libscript/src/math.lcb @@ -322,7 +322,7 @@ end syntax public handler atan2(in pY as Number, in pX as Number) returns Number variable tVar as Number unsafe - MCMathEvalAtan2Number(pX, pY, tVar) + MCMathEvalAtan2Number(pY, pX, tVar) end unsafe return tVar end handler diff --git a/tests/lcs/core/engine/_ungroupandquit.livecodescript b/tests/lcs/core/engine/_ungroupandquit.livecodescript new file mode 100644 index 00000000000..2365b16fcfe --- /dev/null +++ b/tests/lcs/core/engine/_ungroupandquit.livecodescript @@ -0,0 +1,8 @@ +script "_ungroupandquit" +on startup + create button "one" + create button "two" + group button "one" and button "two" + ungroup the last group + quit 0 +end startup \ No newline at end of file diff --git a/tests/lcs/core/engine/engine.livecodescript b/tests/lcs/core/engine/engine.livecodescript index c4a1494063a..5ec52b2d272 100644 --- a/tests/lcs/core/engine/engine.livecodescript +++ b/tests/lcs/core/engine/engine.livecodescript @@ -501,3 +501,19 @@ wait 2 ticks TestAssert "the ticks change with time", the ticks - tTime >= 2 end TestTicks +on TestUngroupAndQuit + -- Bug 21500: run a subprocess in which we ungroup a group and + -- then quit, to test that it no longer crashes + local tStackToRun, tOptions + put the effective filename of me into tStackToRun + set the itemdelimiter to slash + if the environment is not "server" then + put "_ungroupandquit.livecodescript" into item -1 of tStackToRun + if the environment contains "command line" then + put "-ui" into tOptions + end if + end if + TestRunStack tOptions, tStackToRun + TestAssert "ungroup does not crash on quit", the result is empty +end TestUngroupAndQuit +