1 module ws.x.property; 2 3 import 4 std.algorithm, 5 std.array, 6 std.string, 7 std.conv, 8 x11.X, 9 x11.Xlib, 10 x11.Xutil, 11 x11.Xatom, 12 ws.x.atoms, 13 ws.gui.base, 14 ws.wm; 15 16 17 class BaseProperty { 18 19 x11.X.Window window; 20 Atom property; 21 string name; 22 23 abstract void update(); 24 25 } 26 27 28 class PropertyList { 29 30 private BaseProperty[] properties; 31 32 void add(BaseProperty property){ 33 properties ~= property; 34 } 35 36 void remove(BaseProperty property){ 37 properties = properties.without(property); 38 } 39 40 void remove(x11.X.Window window){ 41 properties = properties.filter!(a => a.window != window).array; 42 } 43 44 void update(XPropertyEvent* event){ 45 foreach(property; properties){ 46 if(property.property == event.atom && property.window == event.window){ 47 property.update; 48 } 49 } 50 } 51 52 void update(){ 53 foreach(property; properties){ 54 property.update; 55 } 56 } 57 58 } 59 60 mixin template PropertiesMixin(string name, string atom, int type, bool isList, Args...){ 61 mixin("Property!(type, isList) " ~ name ~ ";"); 62 static if(Args.length) 63 mixin PropertiesMixin!Args; 64 } 65 66 67 struct Properties(Args...) { 68 69 mixin PropertiesMixin!Args; 70 PropertyList propertyList; 71 72 void window(x11.X.Window window){ 73 propertyList = new PropertyList; 74 void init(string name, string atom, int type, bool isList, Args...)(){ 75 mixin("this." ~ name ~ " = new Property!(type, isList)(window, atom, propertyList);"); 76 static if(Args.length) 77 init!Args; 78 } 79 init!Args; 80 } 81 82 void update(Args...)(Args args){ 83 propertyList.update(args); 84 } 85 86 } 87 88 89 class Property(ulong Format, bool List): BaseProperty { 90 91 bool exists; 92 93 static if(Format == XA_CARDINAL || Format == XA_PIXMAP || Format == XA_VISUALID) 94 alias Type = long; 95 static if(Format == XA_ATOM) 96 alias Type = Atom; 97 static if(Format == XA_WINDOW) 98 alias Type = x11.X.Window; 99 static if(Format == XA_STRING) 100 alias Type = string; 101 102 static if(List) 103 alias FullType = Type[]; 104 else 105 alias FullType = Type; 106 107 FullType value; 108 109 void delegate(FullType)[] handlers; 110 111 void opAssign(FullType value){ 112 this.value = value; 113 set(value); 114 } 115 116 void opOpAssign(string op)(void delegate(FullType) handler){ 117 static if(op == "~") 118 handlers ~= handler; 119 else static assert(false, op ~ "= not supported"); 120 } 121 122 alias value this; 123 124 125 this(x11.X.Window window, string name, PropertyList list = null){ 126 if(list) 127 list.add(this); 128 this.window = window; 129 this.name = name; 130 property = XInternAtom(wm.displayHandle, name.toStringz, false); 131 update; 132 } 133 134 this(x11.X.Window window, Atom property, PropertyList list = null){ 135 if(list) 136 list.add(this); 137 this.window = window; 138 this.name = name; 139 this.property = property; 140 update; 141 } 142 143 override void update(){ 144 auto newValue = get; 145 foreach(handler; handlers) 146 handler(newValue); 147 value = newValue; 148 } 149 150 ubyte* raw(ref ulong count){ 151 int di; 152 ulong dl; 153 ubyte* p; 154 Atom da; 155 if(XGetWindowProperty(wm.displayHandle, window, property, 0L, List || is(Type == string) ? long.max : 1, 0, is(Type == string) ? Atoms.UTF8_STRING : Format, &da, &di, &count, &dl, &p) == 0 && p){ 156 exists = true; 157 return p; 158 } 159 exists = false; 160 return null; 161 } 162 163 void rawset(T1, T2)(Atom format, int size, int mode, T1* data, T2 length){ 164 XChangeProperty(wm.displayHandle, window, property, format, size, mode, cast(ubyte*)data, cast(int)length); 165 } 166 167 void request(x11.X.Window window, Type[] data){ 168 XEvent e; 169 e.type = ClientMessage; 170 e.xclient.window = window; 171 e.xclient.message_type = property; 172 e.xclient.format = 32; 173 e.xclient.data.l[0..data.length] = cast(long[])data; 174 XSendEvent(wm.displayHandle, this.window, false, SubstructureNotifyMask|SubstructureRedirectMask, &e); 175 } 176 177 void request(Type[] data){ 178 request(window, data); 179 } 180 181 FullType get(){ 182 ulong count = List ? 0 : 1; 183 auto p = raw(count); 184 if(!p) 185 return FullType.init; 186 FullType value; 187 static if(List){ 188 value = (cast(Type*)p)[0..count].dup; 189 }else static if(is(Type == string)) 190 value = (cast(char*)p)[0..count].to!string; 191 else 192 value = *(cast(Type*)p); 193 XFree(p); 194 return value; 195 } 196 197 void set(FullType data){ 198 static if(List){ 199 rawset(Format, 32, PropModeReplace, data.ptr, data.length); 200 }else static if(is(Type == string)){ 201 rawset(Atoms.UTF8_STRING, 8, PropModeReplace, data.toStringz, data.length); 202 }else{ 203 rawset(Format, 32, PropModeReplace, &data, 1); 204 } 205 } 206 207 } 208 209 210 class PropertyError: Exception { 211 this(string msg){ 212 super(msg); 213 } 214 } 215 216 217 auto dispatchProperty(string name)(x11.X.Window window){ 218 219 struct Proxy { 220 221 T get(T)(){ 222 ulong count; 223 int format; 224 ulong bytes_after; 225 ubyte* p; 226 Atom type; 227 228 if(XGetWindowProperty(wm.displayHandle, window, Atoms.opDispatch!name, 0L, long.max, 0, AnyPropertyType, 229 &type, &format, &count, &bytes_after, &p) == 0 && p){ 230 231 scope(exit) 232 XFree(p); 233 234 import std.stdio, std.traits, std.range; 235 236 static if(is(T == string)){ 237 return (cast(char*)p)[0..count].to!string; 238 }else static if(isIterable!T){ 239 alias Type = ElementType!T; 240 Type[] result; 241 result.length = count; 242 auto casted = cast(Type*)p; 243 foreach(i; 0..count){ 244 result[i] = casted[i]; 245 } 246 return result; 247 }else{ 248 return *(cast(T*)p); 249 } 250 251 } 252 return T.init; 253 } 254 255 void get(T)(void delegate(T) fn){ 256 ulong count; 257 int format; 258 ulong bytes_after; 259 ubyte* p; 260 Atom type; 261 262 if(XGetWindowProperty(wm.displayHandle, window, Atoms.opDispatch!name, 0L, long.max, 0, AnyPropertyType, 263 &type, &format, &count, &bytes_after, &p) == 0 && p){ 264 265 import std.stdio, std.traits, std.range; 266 writeln(type, ' ', format, ' ', count); 267 268 static if(is(T == string)){ 269 fn((cast(char*)p)[0..count].to!string); 270 }else static if(isIterable!T){ 271 alias Type = ElementType!T; 272 Type[] result; 273 result.length = count; 274 auto casted = cast(Type*)p; 275 foreach(i; 0..count){ 276 result[i] = casted[i]; 277 } 278 fn(result); 279 }else{ 280 fn(cast(T*)p); 281 } 282 283 XFree(p); 284 } 285 } 286 287 void get(T)(void function(T) fn){ 288 import std.functional; 289 get(fn.toDelegate); 290 } 291 292 } 293 294 return Proxy(); 295 296 } 297 298 299 auto props(x11.X.Window window){ 300 301 struct Dispatcher { 302 303 auto opDispatch(string name)(){ 304 return dispatchProperty!name(window); 305 } 306 307 } 308 309 return Dispatcher(); 310 311 } 312