[For David, aarku] Carbon Events

Luminary
Posts: 5,143
Joined: 2002.04
Post: #1
This is a source file from an old project of mine showing how to use Carbon events for game input. Make of it what you can...

Code:
#include <Carbon/Carbon.h>

#include "EventHandler.hpp"

#include "MacKeyCodes.hpp"

namespace COSI {

int shiftLevel = 0;

ScreenDimension EventHandler::getScreenResolution() {
    ScreenDimension result;
    return result;
}


#define CARBON_ERROR(err) if (err != noErr) { fatalError("Carbon error `%d'\n", err); }

static const EventTypeSpec KEY_EVENTS[] = {
  { kEventClassKeyboard, kEventRawKeyDown },
  { kEventClassKeyboard, kEventRawKeyUp },
  { kEventClassKeyboard, kEventRawKeyModifiersChanged }
};

static const EventTypeSpec MOUSE_EVENTS[] = {
  { kEventClassMouse, kEventMouseDown },
  { kEventClassMouse, kEventMouseUp },
  { kEventClassMouse, kEventMouseMoved },
  { kEventClassMouse, kEventMouseDragged }
};

static const EventTypeSpec WINDOW_EVENTS[] = {
  { kEventClassWindow, kEventWindowBoundsChanged },
    { kEventClassWindow, kEventWindowFocusAcquired },
  { kEventClassWindow, kEventWindowFocusRelinquish },
  { kEventClassWindow, kEventWindowCollapsed },
  { kEventClassWindow, kEventWindowExpanded }
};


Key macToCOSIKeyCode(UInt32 macKeyCode);

Key macToCOSIKeyCode(UInt32 macKeyCode) {
  if (macKeyCode >= NUM_KEY_CODES) {
    return KEY_UNKNOWN;
  }
  Key result = KEY_CODES[macKeyCode];
  
  //debugMessage("Translating %lx to %s\n", macKeyCode, keyName(result).c_str());
  
  return result;
}

OSStatus keyHandler(EventHandlerCallRef hcr, EventRef event, void* p);
OSStatus mouseHandler(EventHandlerCallRef hcr, EventRef event, void* p);
OSStatus appWindowHandler(EventHandlerCallRef hcr, EventRef event, void* p);

void idleTimer(EventLoopTimerRef timer, void *p);

OSStatus keyHandler(EventHandlerCallRef hcr, EventRef event, void* p) {
  EventHandler* handler = (EventHandler*)(p);

  if (GetEventClass(event) != kEventClassKeyboard) {
    fatalError("Carbon error: wrong event class given to handler\n");
  }
  switch(GetEventKind(event)) {
  case kEventRawKeyDown: {
    UInt32 keyCode;
    
    OSStatus error = GetEventParameter(event,
                                       kEventParamKeyCode,
                                       typeUInt32,
                                       NULL,
                                       sizeof(UInt32),
                                       NULL,
                                       &keyCode);
    CARBON_ERROR(error);
    handler->keyPressed(macToCOSIKeyCode(keyCode));
  } break;
  case kEventRawKeyUp: {
    UInt32 keyCode;
    
    OSStatus error = GetEventParameter(event,
                                       kEventParamKeyCode,
                                       typeUInt32,
                                       NULL,
                                       sizeof(UInt32),
                                       NULL,
                                       &keyCode);
    CARBON_ERROR(error);
    handler->keyReleased(macToCOSIKeyCode(keyCode));
  } break;
  case kEventRawKeyModifiersChanged: {
    static UInt32 modifiers = 0;
    UInt32 newModifiers;
    
    OSStatus error = GetEventParameter(event,
                                       kEventParamKeyModifiers,
                                       typeUInt32,
                                       NULL,
                                       sizeof(UInt32),
                                       NULL,
                                       &newModifiers);
    CARBON_ERROR(error);
    
    UInt32 changed = modifiers ^ newModifiers;
      
    if ((changed & cmdKey) != 0) {
      if ((modifiers & cmdKey) != 0) {
        handler->keyReleased(KEY_COMMAND);
      } else {
        handler->keyPressed(KEY_COMMAND);
      }
    }
    if ((changed & (shiftKey | rightShiftKey)) != 0) {
      if ((modifiers & (shiftKey | rightShiftKey)) != 0) {
        handler->keyReleased(KEY_SHIFT);
      } else {
        handler->keyPressed(KEY_SHIFT);
      }
    }
    if ((changed & (optionKey | rightOptionKey)) != 0) {
      if ((modifiers & (optionKey | rightOptionKey)) != 0) {
        handler->keyReleased(KEY_OPTION);
      } else {
        handler->keyPressed(KEY_OPTION);
      }
    }
    if ((changed & (controlKey | rightControlKey)) != 0) {
      if ((modifiers & (controlKey | rightControlKey)) != 0) {
        handler->keyReleased(KEY_CTRL);
      } else {
        handler->keyPressed(KEY_CTRL);
      }
    }
    if ((changed & alphaLock) != 0) {
      if ((modifiers & alphaLock) != 0) {
        handler->keyReleased(KEY_CAPS_LOCK);
      } else {
        handler->keyPressed(KEY_CAPS_LOCK);
      }
    }
    
    modifiers = newModifiers;
    
  } break;
  default:
    fatalError("Carbon error: wrong event kind given to handler\n");
  }
  
  return noErr;
}

OSStatus mouseHandler(EventHandlerCallRef hcr, EventRef event, void* p) {
  static ButtonMask currentButtons = 0;

  EventHandler* handler = (EventHandler*)(p);

  if (GetEventClass(event) != kEventClassMouse) {
    fatalError("Carbon error: wrong event class given to handler\n");
  }
  switch(GetEventKind(event)) {
  case kEventMouseDown: {
    Point where;
    OSStatus error;
    error = GetEventParameter(event,
                              kEventParamMouseLocation,
                              typeQDPoint,
                              NULL,
                              sizeof(where),
                              NULL,
                              &where);
    CARBON_ERROR(error);
    GlobalToLocal(&where);
    
    EventMouseButton button;
    error = GetEventParameter(event,
                              kEventParamMouseButton,
                              typeMouseButton,
                              NULL,
                              sizeof(button),
                              NULL,
                              &button);
    CARBON_ERROR(error);
    
    //debugMessage("Mouse button %d pressed\n", button);
    
    currentButtons |= nthMouseButton(button);
    
    handler->mousePressed(MouseLocation(where.h, where.v),
                          currentButtons,
                          nthMouseButton(button));
  } break;
  case kEventMouseUp: {
    Point where;
    OSStatus error;
    error = GetEventParameter(event,
                              kEventParamMouseLocation,
                              typeQDPoint,
                              NULL,
                              sizeof(where),
                              NULL,
                              &where);
    CARBON_ERROR(error);
    GlobalToLocal(&where);
    
    EventMouseButton button;
    error = GetEventParameter(event,
                              kEventParamMouseButton,
                              typeMouseButton,
                              NULL,
                              sizeof(button),
                              NULL,
                              &button);
    CARBON_ERROR(error);
    
    //debugMessage("Mouse button %d released\n", button);
    
    currentButtons &= ~nthMouseButton(button);
    
    handler->mouseReleased(MouseLocation(where.h, where.v),
                           currentButtons,
                           nthMouseButton(button));
  } break;
  case kEventMouseMoved: case kEventMouseDragged: {
    Point location, delta;
    OSStatus error;
    
    error = GetEventParameter(event,
                              kEventParamMouseLocation,
                              typeQDPoint,
                              NULL,
                              sizeof(location),
                              NULL,
                              &location);
    CARBON_ERROR(error);
    GlobalToLocal(&location);
    
    // kEventParamMouseDelta doesn't work on MacOS < X, so this code
    // will need to be replaced if Carbon COSI is to work on 8.6 or 9.x
    error = GetEventParameter(event,
                              kEventParamMouseDelta,
                              typeQDPoint,
                              NULL,
                              sizeof(delta),
                              NULL,
                              &delta);
    CARBON_ERROR(error);
    
    handler->mouseMoved(MouseLocation(location.h, location.v),
                        MouseDelta(delta.h, delta.v),
                        currentButtons);
  } break;
  default:
    fatalError("Carbon error: wrong event kind given to handler\n");
  }
  
  return noErr;
}

OSStatus appWindowHandler(EventHandlerCallRef hcr, EventRef event, void* p) {
  EventHandler* handler = (EventHandler*)(p);

  if (GetEventClass(event) != kEventClassWindow) {
    fatalError("Carbon error: wrong event class given to handler\n");
  }
  switch(GetEventKind(event)) {
  case kEventWindowBoundsChanged: {
    Rect bounds;
    OSStatus error;
    error = GetEventParameter(event,
                              kEventParamCurrentBounds,
                              typeQDRectangle,
                              NULL,
                              sizeof(bounds),
                              NULL,
                              &bounds);
    CARBON_ERROR(error);
  
    handler->windowResized(ScreenDimension(bounds.right - bounds.left, bounds.bottom - bounds.top));
  } break;
    case kEventWindowFocusAcquired: {
    handler->windowFocus(ACQUIRED);
  } break;
  case kEventWindowFocusRelinquish: {
    handler->windowFocus(LOST);
  } break;
  case kEventWindowCollapsed: {
    handler->windowStatus(MINIMIZED);
  } break;
  case kEventWindowExpanded: {
    handler->windowStatus(UNMINIMIZED);
  } break;
  default:
    fatalError("Carbon error: wrong event kind given to handler\n");
  }
  
  return noErr;
}

void idleTimer(EventLoopTimerRef timer, void *p) {
  EventHandler* handler = (EventHandler*)(p);
  handler->idle();
}
  
EventHandler::EventHandler() {
  OSStatus error;
  error = InstallApplicationEventHandler(NewEventHandlerUPP(keyHandler),
                                         GetEventTypeCount(KEY_EVENTS),
                                         KEY_EVENTS,
                                         this,
                                         NULL);
  CARBON_ERROR(error);
  
  error = InstallApplicationEventHandler(NewEventHandlerUPP(mouseHandler),
                                         GetEventTypeCount(MOUSE_EVENTS),
                                         MOUSE_EVENTS,
                                         this,
                                         NULL);
  CARBON_ERROR(error);
  
  error = InstallApplicationEventHandler(NewEventHandlerUPP(appWindowHandler),
                                         GetEventTypeCount(WINDOW_EVENTS),
                                         WINDOW_EVENTS,
                                         this,
                                         NULL);
  CARBON_ERROR(error);
  
  error = InstallEventLoopTimer(GetMainEventLoop(),              //inEventLoop
                                0,                               //inFireDelay
                                5 * kEventDurationMillisecond,   //inInterval (200 Hz)
                                NewEventLoopTimerUPP(idleTimer), //inTimerProc
                                this,                            //inTimerData,
                                NULL                             //outTimer
                               );
  CARBON_ERROR(error);
}

EventHandler::~EventHandler() {
}

void EventHandler::handleEvents() {
  RunApplicationEventLoop();
}

}
Quote this message in a reply
Moderator
Posts: 453
Joined: 2008.04
Post: #2
Great post Keith! I was going through an old Carbon project.. and someone had a question about using IME input to enter non-english characters, like é. Can this carbon approach be used with IME?

(No one give me crap about resurrecting an old thread. I'm on topic!)

Howling Moon Software - CrayonBall for Mac and iPhone, Contract Game Dev Work
Quote this message in a reply
Sage
Posts: 1,482
Joined: 2002.09
Post: #3
They might give you crap about resurrecting Carbon though. Wink

Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #4
This is all deprecated these days... and had nothing to do with input methods in the first place.

For a modern equivalent of this code, see https://github.com/elmindreda/glfw/blob/...a_window.m (but it still doesn't deal with input methods).
Quote this message in a reply
⌘-R in Chief
Posts: 1,261
Joined: 2002.05
Post: #5
Someone give this man a trophy for the oldest thread revival ever. LOL
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Carbon events in windowed/full screen mode. Help needed. Anton Petrov 1 3,812 Dec 18, 2008 05:35 AM
Last Post: DoG
  Simulating "system idle" events in Carbon applications ascotti 4 3,749 May 2, 2006 03:22 PM
Last Post: OneSadCookie
  Carbon Key Events Nickolei 7 5,435 Nov 17, 2002 03:37 PM
Last Post: codemattic
  Carbon Keyboard Events IBethune 7 6,094 Oct 22, 2002 05:58 AM
Last Post: IBethune
  Carbon events (again) sealfin 2 3,873 Jul 1, 2002 06:34 AM
Last Post: sealfin