1 2 module i3ipc.protocol; 3 4 import std.exception : enforce; 5 import std.socket : Socket; 6 import std.conv : to; 7 import std.json : JSONValue, parseJSON; 8 9 import i3ipc.socket; 10 11 align(1) struct Header 12 { 13 align (1): 14 /** Never change this, only on major IPC breakage (don’t do that) */ 15 char[6] magic = "i3-ipc"; 16 uint payloadSize; 17 union 18 { 19 uint rawType; 20 RequestType requestType; 21 ResponseType responseType; 22 EventType eventType; 23 } 24 } 25 26 enum RequestType : uint 27 { 28 Command = 0, 29 GetWorkspaces, 30 Subscribe, 31 GetOutputs, 32 GetTree, 33 GetMarks, 34 GetBarConfig, 35 GetVersion 36 } 37 38 enum ResponseType : uint 39 { 40 Command = 0, 41 Workspaces, 42 Subscribe, 43 Outputs, 44 Tree, 45 Marks, 46 BarConfig, 47 Version 48 } 49 50 enum EventMask = (1 << 31); 51 enum EventType : uint 52 { 53 Workspace = (EventMask | 0), 54 Output = (EventMask | 1), 55 Mode = (EventMask | 2), 56 Window = (EventMask | 3), 57 BarConfigUpdate = (EventMask | 4), 58 Binding = (EventMask | 5) 59 } 60 61 string toString(EventType type) 62 { 63 switch (type) { 64 case EventType.Workspace: return "workspace"; 65 case EventType.Output: return "output"; 66 case EventType.Mode: return "mode"; 67 case EventType.Window: return "window"; 68 case EventType.BarConfigUpdate: return "barconfig_update"; 69 case EventType.Binding: return "binding"; 70 default: assert(0); 71 } 72 } 73 74 void sendMessage(Socket socket, RequestType type, immutable(void)[] message = []) 75 { 76 Header header; 77 header.payloadSize = to!uint(message.length); 78 header.requestType = type; 79 socket.send((cast(void*) &header)[0 .. Header.sizeof]); 80 if (message.length) socket.send(message); 81 } 82 83 JSONValue receiveMessage(Socket socket, ResponseType type) 84 { 85 auto header = socket.receiveExactly!Header; 86 auto payload = socket.receiveExactly(new ubyte[header.payloadSize]); 87 88 enforce(type == header.responseType); 89 return parseJSON(payload); 90 }