I finally have a working Permebuf IPC example! These are one way "mini-messages" (messages that are <=32 bytes and can be transfered in registers) between two services. The base classes (MouseDriver, MouseDriver::Server, MouseDriver::SetMouseListenerMessage, etc.) are all generated from a .permebuf file.
Basic mouse driver (I removed the actual hardware code, to highlight just the Permebuf service code):
Code:
class PS2MouseDriver : public MouseDriver::Server {
public:
PS2MouseDriver() :
mouse_bytes_received_(0),
last_button_state_{false, false, false} {}
virtual ~PS2MouseDriver() {
if (mouse_captor_) {
// Tell the captor we had to let the mouse go.
mouse_captor_->SendOnMouseRelease(MouseListener::OnMouseReleaseMessage());
}
}
void HandleMouseInterrupt() {
// ...
ProcessMouseMessage(...);
}
virtual void HandleSetMouseListener(ProcessId,
const MouseDriver::SetMouseListenerMessage& message) {
if (!mouse_captor_) {
// Let the old captor know the mouse has escaped.
mouse_captor_->SendOnMouseRelease(MouseListener::OnMouseReleaseMessage());
}
if (message.HasNewListener()) {
mouse_captor_ = std::make_unique<MouseListener>(message.GetNewListener());
// Let our captor know they have taken the mouse captive.
mouse_captor_->SendOnMouseTakenCaptive(
MouseListener::OnMouseTakenCaptiveMessage());
} else {
mouse_captor_.reset();
}
}
private:
// ...
// The last known state of the mouse buttons.
bool last_button_state_[3];
// The service we should sent mouse events to.
std::unique_ptr<MouseListener> mouse_captor_;
// Processes the mouse message.
void ProcessMouseMessage(uint8 status, uint8 offset_x, uint8 offset_y) {
// Read if the mouse has moved.
int16 delta_x = (int16)offset_x - (((int16)status << 4) & 0x100);
int16 delta_y = -(int16)offset_y + (((int16)status << 3) & 0x100);
// Read the left, middle, right buttons.
bool buttons[3] = {
(status & (1)) == 1,
(status & (1 << 2)) == 4,
(status & (1 << 1)) == 2};
if ((delta_x != 0 || delta_y != 0) && mouse_captor_) {
// Send our captor a message that the mouse has moved.
MouseListener::OnMouseMoveMessage message;
message.SetDeltaX(static_cast<float>(delta_x));
message.SetDeltaY(static_cast<float>(delta_y));
mouse_captor_->SendOnMouseMove(message);
}
for (int button_index : {0,1,2}) {
if (buttons[button_index] != last_button_state_[button_index]) {
last_button_state_[button_index] = buttons[button_index];
if (mouse_captor_) {
// Send our captor a message that a mouse button has changed state.
MouseListener::OnMouseButtonMessage message;
switch (button_index) {
case 0: message.SetButton(MouseButton::Left); break;
case 1: message.SetButton(MouseButton::Middle); break;
case 2: message.SetButton(MouseButton::Right); break;
}
message.SetIsPressedDown(buttons[button_index]);
mouse_captor_->SendOnMouseButton(message);
}
}
}
}
};
// Global instance of the mouse driver.
std::unique_ptr<PS2MouseDriver> mouse_driver;
void InterruptHandler() {
mouse_driver->HandleMouseInterrupt();
}
int main() {
mouse_driver = std::make_unique<PS2MouseDriver>();
// ...
// Listen to the interrupts.
RegisterInterruptHandler(1, InterruptHandler);
RegisterInterruptHandler(12, InterruptHandler);
perception::TransferToEventLoop();
return 0;
}
A program that that creates a MouseListener, implements just the methods it cares about (OnMouseMove, OnMouseButton), waits for an instance of the MouseDriver to appear (so it doesn't matter which order you start the two programs), and sends a reference to the listener class to another process.
Code:
class MyMouseListener : public MouseListener::Server {
public:
void HandleOnMouseMove(
ProcessId, const MouseListener::OnMouseMoveMessage& message) override {
std::cout << "X:" << message.GetDeltaX() <<
" Y:" << message.GetDeltaY() << std::endl;
}
void HandleOnMouseButton(
ProcessId, const MouseListener::OnMouseButtonMessage& message) override {
std::cout << "Button: " << (int)message.GetButton() <<
" Down: " << message.GetIsPressedDown() << std::endl;
}
};
int main() {
MyMouseListener mouse_listener;
MessageId listener = MouseDriver::NotifyOnEachNewInstance(
[&] (MouseDriver mouse_driver) {
// Tell the mouse driver to send us mouse messages.
MouseDriver::SetMouseListenerMessage message;
message.SetNewListener(mouse_listener);
mouse_driver.SendSetMouseListener(message);
// We only care about one instance. We can stop
// listening now.
MouseDriver::StopNotifyingOnEachNewInstance(
listener);
});
perception::TransferToEventLoop();
return 0;
}
Baby steps!