OSXOperatingSystem.st
author Claus Gittinger <cg@exept.de>
Fri, 17 Feb 2017 10:25:31 +0100
changeset 21480 20b4ddb4ba7a
parent 21455 a657a28cab85
child 21580 b0c093983502
permissions -rw-r--r--
#FEATURE by cg class: Object added: #isURL

"
 COPYRIGHT (c) 2013 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libbasic' }"

"{ NameSpace: Smalltalk }"

UnixOperatingSystem subclass:#OSXOperatingSystem
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	category:'OS-Unix'
!

!OSXOperatingSystem primitiveDefinitions!
%{

#ifndef __OSX__
# define NO_QUARTZ
# define NO_COCOA
#endif

#ifndef NO_COCOA

#   undef Array
#   undef Number
#   undef Method
#   undef Point
#   undef Rectangle
#   undef Block
#   undef String
#   undef Character
#   undef Message
#   undef Object
#   undef Context
#   undef Time
#   undef Date
#   undef Set
#   undef Signal
#   undef Delay
#   undef NameSpace
#   undef Process
#   undef Processor
#   undef Class
#   undef FixedPoint
#   undef true
#   undef false

#   undef INT
#   undef UINT

#include <ApplicationServices/ApplicationServices.h>
#include <CoreFoundation/CoreFoundation.h>
// #include <Foundation/Foundation.h>

#include <stdlib.h>
#include <stdio.h>
#include <IOKit/graphics/IOGraphicsLib.h>

#include <objc/runtime.h>
#include <objc/message.h>

#  ifdef __DEF_Array
#   define Array __DEF_Array
#  endif
#  ifdef __DEF_Number
#   define Number __DEF_Number
#  endif
#  ifdef __DEF_Method
#   define Method __DEF_Method
#  endif
#  ifdef __DEF_Point
#   define Point __DEF_Point
#  endif
#  ifdef __DEF_Rectangle
#   define Rectangle __DEF_Rectangle
#  endif
#  ifdef __DEF_Block
#   define Block __DEF_Block
#  endif
#  ifdef __DEF_String
#   define String __DEF_String
#  endif
#  ifdef __DEF_Character
#   define Character __DEF_Character
#  endif
#  ifdef __DEF_Message
#   define Message __DEF_Message
#  endif
#  ifdef __DEF_Object
#   define Object __DEF_Object
#  endif
#  ifdef __DEF_Context
#   define Context __DEF_Context
#  endif
#  ifdef __DEF_Date
#   define Date __DEF_Date
#  endif
#  ifdef __DEF_Time
#   define Time __DEF_Time
#  endif
#  ifdef __DEF_Set
#   define Set __DEF_Set
#  endif
#  ifdef __DEF_Signal
#   define Signal __DEF_Signal
#  endif
#  ifdef __DEF_Delay
#   define Delay __DEF_Delay
#  endif
#  ifdef __DEF_NameSpace
#   define NameSpace __DEF_NameSpace
#  endif
#  ifdef __DEF_Process
#   define Process __DEF_Process
#  endif
#  ifdef __DEF_Processor
#   define Processor __DEF_Processor
#  endif
#  ifdef __DEF_Class
#   define Class __DEF_Class
#  endif
#  ifdef __DEF_FixedPoint
#   define FixedPoint __DEF_FixedPoint
#  endif
#  ifdef __DEF_true
#   undef true
#   define true __DEF_true
#  endif
#  ifdef __DEF_false
#   undef false
#   define false __DEF_false
#  endif

#  define INT STX_INT
#  define UINT STX_UINT

#endif // NP_COCOA

%}
! !

!OSXOperatingSystem primitiveFunctions!
%{

#ifndef NO_COCOA

CGEventRef
myCGEventCallback(CGEventTapProxy proxy, CGEventType type,
		  CGEventRef event, void *refcon)
{
    // Paranoid sanity check.
    if ((type != kCGEventKeyDown) && (type != kCGEventKeyUp))
	return event;

    // The incoming keycode.
    CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);

    //Keypress code goes here.

    // We must return the event for it to be useful.
    return event;
}

#endif // NO_COCOA

%}
! !

!OSXOperatingSystem class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2013 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    a small number of OS-X specific redefinitions here.

    [Author:]
	Claus Gittinger
"
! !

!OSXOperatingSystem class methodsFor:'initialization'!

initializeCodeset
    super initializeCodeset.
    Codeset := #'utf8-mac'.
    CodesetEncoder := nil.
! !

!OSXOperatingSystem class methodsFor:'cocoa - events'!

receiveNextEvent
%{
#ifndef NO_COCOA
# if 0
    CFMachPortRef      eventTap;
    CGEventMask        eventMask;
    CFRunLoopSourceRef runLoopSource;

    // Create an event tap. We are interested in key presses.
    eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp));
    eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0,
				eventMask, NULL, myCGEventCallback);
    if (!eventTap) {
	fprintf(stderr, "failed to create event tap\n");
    } else{
	fprintf(stderr, "ok\n");
    }

    // Create a run loop source.
    runLoopSource = CFMachPortCreateRunLoopSource( kCFAllocatorDefault, eventTap, 0);
#  if 0
    // Add to the current run loop. kCFRunLoopCommonModes);
#  endif
    // Enable the event tap.
    CGEventTapEnable(eventTap, true);

# endif
#endif // NO_COCOA
%}.
    self primitiveFailed
! !

!OSXOperatingSystem class methodsFor:'dummy shell operations'!

openApplicationHelperCommand
    "Return a command line helper to open a default application for file or URL"

    (self canExecuteCommand: 'open') ifTrue:[
	^ 'open'
    ].
    ^ nil

    "Created: / 13-01-2015 / 09:23:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

openTerminalWithCommand:shellCommand inBackground:inBackground
    "open a new terminal, which executes a command"

    |cmd|

    cmd := 'osascript -e ''tell application "Terminal" to do script "%1"''' bindWith:shellCommand.

    inBackground ifTrue:[
	^ self
	    startProcess:cmd
	    inputFrom:nil
	    outputTo:nil
	    errorTo:nil
	    auxFrom:nil
	    environment:nil
	    inDirectory:nil
    ] ifFalse:[
	^ self executeCommand:cmd
    ].

    "
     OSXOperatingSystem openTerminalWithCommand:'ls -l' inBackground:true
    "
! !

!OSXOperatingSystem class methodsFor:'file queries'!

caseSensitiveFilenames
    "return true, if the OS has caseSensitive file naming.
     On MSDOS, this will return false;
     on a real OS, we return true.
     Be aware, that OSX can be configured to be either.
     Also, that it actually depends on the mounted volume"

    "/ actually, this is wrong and depends on the mounted volume;
    "/ so we need a query for a particular directory (and/or volume).
    ^ false

    "Modified: / 5.6.1998 / 18:35:18 / cg"
!

defaultPackagePath
    "redefined to add /Application and /Library stuff"
    "called by Smalltalk initSystemPath"
    "self defaultPackagePath"

    |path executablePath executableDir packagesDir
     libDir appDir versionsDir vsnDirName vsnDir|

    path := super defaultPackagePath.

    executablePath := OperatingSystem pathOfSTXExecutable.
    executablePath notNil ifTrue:[
	executableDir := executablePath asFilename directory.
	packagesDir := executableDir directory directory / 'Packages'.
	packagesDir exists ifTrue:[
	    packagesDir := packagesDir pathName.
	    (path includes:packagesDir) ifFalse:[
		path add:packagesDir.
	    ].
	].
	libDir := '/Library/Frameworks/SmalltalkX.framework' asFilename.
	libDir exists ifTrue:[
	    versionsDir := libDir / 'Versions'.
	    versionsDir exists ifTrue:[
		vsnDirName := '%1.%2.%3'
				    bindWith:Smalltalk majorVersionNr
				    with:Smalltalk minorVersionNr
				    with:Smalltalk revisionNr.
		vsnDir := versionsDir / vsnDirName.
		vsnDir exists ifTrue:[
		    vsnDir := vsnDir pathName.
		    (path includes:vsnDir) ifFalse:[
			path add:vsnDir.
		    ].
		].
	    ].
	].
	appDir := '/Applications/SmalltalkX/' asFilename.
	appDir exists ifTrue:[
	    versionsDir := appDir / 'Versions'.
	    versionsDir exists ifTrue:[
		vsnDirName := '%1.%2.%3'
				    bindWith:Smalltalk majorVersionNr
				    with:Smalltalk minorVersionNr
				    with:Smalltalk revisionNr.
		vsnDir := versionsDir / vsnDirName.
		vsnDir exists ifTrue:[
		    vsnDir := vsnDir pathName.
		    (path includes:vsnDir) ifFalse:[
			path add:vsnDir.
		    ].
		].
	    ].
	].
    ].
    ^ path
!

getDriveList
    "return a list of volumes in the system."

    ^ ('/Volumes' asFilename directoryContents)
      , super getDriveList
!

getTrashDirectory
    "get the name of a trash folder (if the OS supports it),
     or nil, if not.
     Must be redefined to return non nil in concrete operating systems"

    ^ '~/.Trash'
!

pathNameForDrive:driveName
    "given a drive name, return the pathname to open it as a directory.
     For Windows, this is the driveName itself.
     For OSX, '/Volumes' is prepended.
     Other OSs might prepent the pount point (i.e. /mnt/)"

    driveName isNil ifTrue:[^ nil].
    driveName asFilename isAbsolute ifTrue:[^ driveName].
    ^ '/Volumes/',driveName
!

supportsVolumes
    "return true if the os support a list of drives/volumes
     (here we can return a list of mounted drives in /Volumes)"

    ^ true
! !

!OSXOperatingSystem class methodsFor:'quartz - screen'!

getFrameBufferImage:displayNr
    "returns the framebuffer as an image object"

    |width height bytesPerPixel address pixels depth pad img|

%{
#ifndef NO_QUARTZ
    CGImageRef image_ref = CGDisplayCreateImage(CGMainDisplayID());
    CGDataProviderRef provider = CGImageGetDataProvider(image_ref);
    CFDataRef dataref = CGDataProviderCopyData(provider);
    size_t c_width, c_height;

    c_width = CGImageGetWidth(image_ref);
    c_height = CGImageGetHeight(image_ref);
    size_t c_bytesPerPixel = CGImageGetBitsPerPixel(image_ref) / 8;
    pixels = __BYTEARRAY_UNINITIALIZED_NEW_INT(c_width * c_height * c_bytesPerPixel);
    // uint8 *pixels = malloc(c_width * c_height * c_bytesPerPixel);
    memcpy(__byteArrayVal(pixels), CFDataGetBytePtr(dataref), c_width * c_height * c_bytesPerPixel);
    CFRelease(dataref);
    CGImageRelease(image_ref);

    width = __MKUINT( c_width );
    height = __MKUINT( c_height );
    bytesPerPixel = __MKUINT( c_bytesPerPixel );

// CGDisplayBaseAddress is deprecated
//
//    int      i, saveFD = -1;
//    uint32_t rowBytes, rowUInt32s, *screen;
//
//    CGDirectDisplayID targetDisplay = 0;
//
//    // populate targetDisplay as in Figure 10.23
//    // use listDisplays() from Figure 10.23
//    // ...
//
//    screen = (uint32_t *)CGDisplayBaseAddress(targetDisplay);
//    rowBytes = CGDisplayBytesPerRow(targetDisplay);
//    rowUInt32s = rowBytes / 4;
//    width = __MKUINT( CGDisplayPixelsWide(targetDisplay) );
//    height = __MKUINT( CGDisplayPixelsHigh(targetDisplay) );

//    for (i = 0; i < height; i++)
//        write(saveFD, screen + i * rowUInt32s, width * sizeof(uint32_t));
//
#endif // NO_QUARTZ

%}.
    width isNil ifTrue:[
	^ self primitiveFailed
    ].
    Transcript printf:'w:%d h:%d bpp:%d\n' withAll:{ width . height . bytesPerPixel }.

    depth := bytesPerPixel * 8.

    Transcript printf:'d:%d\n' withAll:{ depth }.

    "/ MUST swap r and b channels - sigh
    "/ pixels swapBGRtoRGB

    img := Image extent:(width @ height) depth:depth bits:pixels.
    depth >= 24 ifFalse:[
	"/ check what we get here...
	^ self primitiveFailed:'unsupported depth'
    ].

    img bitsPerSample:#[8 8 8].
    img samplesPerPixel:3.
    img photometric:#rgb.
    ^ img
! !

!OSXOperatingSystem class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !