1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
|
#ifndef __OSDSERVER_OSDOBJECTSBASE_H
#define __OSDSERVER_OSDOBJECTSBASE_H
#include <map>
#include <set>
#include <queue>
#include <vdr/tools.h>
#include <vdr/thread.h>
#include <vdr/osdbase.h>
#include <vdr/menuitems.h>
#include "tools.h"
class cServerContext;
class cOsdServerObject {
// Generic base class for named and unnamed objects of the network interface.
// These objects live in the network thread
// These objects may 'own' a VDR main thread object.
// The OsdServerObject holds a copy of the state of the VDR object, and
// can push its state to the VDR object, or create it if it doesnt exist yet.
// The VDR object can detach if its lifetime ends.
// Before deleting this object, always call Detach().
private:
int id;
// Unique ID identifying any object
static int nextId;
// Common ID counter
cServerContext *context;
// The 'owning' context. Deleting this context deletes the object.
// The object can be present in more contexts.
public:
cOsdServerObject();
virtual ~cOsdServerObject();
// IDs to identify certain object instances from base class
enum enumClassId {
clsidMenu,
clsidOsdItem,
clsidMenuEditStrItem,
clsidMenuEditIntItem,
clsidMenuEditListItem
};
virtual enumClassId ClassId()=0;
virtual bool IsMenuItem() { return false; }
// Generic cast function that checks the id.
// Use as baseobject->Cast<type>()
template<class T> T* Cast() {
if (ClassId() != T::classid) return NULL;
return dynamic_cast<T*>(this);
}
// Context object name or NULL if not named
inline cString Name();
class cEvent {
public:
enum eventType { NullEvent, KeyEvent, CloseEvent, FocusEvent, BlurEvent, EditEvent } type;
eKeys key;
cEvent() { type = NullEvent; key = kNone; }
cEvent(eventType t) { type = t; key = kNone; }
cEvent(eKeys k) { type = KeyEvent; key = k; }
bool operator<(const cEvent &ev) const {
if (type == KeyEvent && ev.type == KeyEvent)
return key < ev.key;
return type < ev.type;
}
};
class cTriggeredEvent : public cEvent {
public:
cOsdServerObject *source;
cTriggeredEvent(cOsdServerObject *src, cEvent ev) : cEvent(ev) { source=src; }
cTriggeredEvent(cOsdServerObject *src, eKeys k) : cEvent(k) { source=src; }
cTriggeredEvent(cOsdServerObject *src, eventType t) : cEvent(t) { source=src; }
cTriggeredEvent() { source=NULL; }
};
protected:
std::set<cEvent> enabledEvents;
std::queue<cTriggeredEvent> eventQueue;
// These are protected by LockShared mutex,
// unless both threads are synchronized anyway.
bool IsEventEnabled(cEvent event) {
std::set<cEvent>::iterator i = enabledEvents.find(event);
return i != enabledEvents.end();
}
public:
void TriggerEvent(cOsdServerObject *src, cEvent ev) {
if (src->IsEventEnabled(ev)) eventQueue.push(cTriggeredEvent(src,ev));
}
void TriggerEvent(cEvent ev) {
TriggerEvent(this,ev);
}
virtual bool EnableEvent(cEvent event) {
enabledEvents.insert(event);
return true;
}
virtual bool PollEvent(cTriggeredEvent &Event) { return false; }
// Primitive event handling
inline void SetFocusObject();
// Set object as focused
inline void UnsetFocusObject();
// Set object as focused
inline bool IsFocusObject();
// Check whether object is focused
friend class cServerContext;
};
struct ltcstrcase {
bool operator()(const cString &s1, const cString &s2) const {
return strcasecmp(s1, s2) < 0;
}
};
class cServerContext {
// Manage a collection of named OsdServerObjects.
typedef std::map<cString, cOsdServerObject*, ltcstrcase> ObjectList;
ObjectList objects; // Maps name to object
typedef std::map<int, cString> IdList;
IdList idList; // Maps id to name
cServerContext *localContext;
cServerContext *parentContext;
cOsdServerObject *focusObject;
public:
cOsdServerObject* GetName(const char *Name);
// Get object by name
cString GetNameById(int id) {
// Get object name by id
IdList::iterator i = idList.find(id);
return (i != idList.end()) ? i->second : cString::sprintf("_%i", id);
}
void Add(cOsdServerObject *Object, cString Name);
// Also sets the name of the object
void Remove(cOsdServerObject *Object);
// Removes an object from the context
cServerContext* MakeLocalContext();
// Create local context as alias of this one
cServerContext* GetParentContext() { return parentContext; }
// Get parent of this context, or NULL for topmost context
void DestroyLocalContext();
// Delete local contexts of this one
cOsdServerObject* GetFocusObject();
// Get the currently focused object (not menu items, just menus)
cServerContext();
virtual ~cServerContext();
friend class cOsdServerObject;
};
// Forwarded inlines of cOsdServerObject
inline cString cOsdServerObject::Name() {
return context ? context->GetNameById(id) : cString();
}
inline void cOsdServerObject::SetFocusObject() {
if (context) context->focusObject = this;
}
inline void cOsdServerObject::UnsetFocusObject() {
if (context && context->focusObject == this)
context->focusObject = NULL;
}
inline bool cOsdServerObject::IsFocusObject() {
return context ? (this == context->focusObject) : false;
}
class cOsdServerMenu;
class cOsdServerMenuItem : public cListObject, public cOsdServerObject {
// Base class for menu items
cOsdServerMenu *menu;
protected:
virtual cOsdItem* GetItem()=0;
cOsdServerMenuItem() { menu=NULL; }
public:
cOsdServerMenu* GetMenu() { return menu; }
virtual bool IsMenuItem() { return true; }
virtual void ItemUpdate()=0;
// Forwarder to cShadowBase if of that class
virtual void ItemDetach()=0;
// Forwarder to cShadowBase if of that class
friend class cOsdServerMenu; // to set menu
};
class cShadowBase {
// Abstract base class for shadowed objects. Acts as base class for cShadowTemplate
protected:
bool Dirty;
// =true means that this object has changes that are not yet Update()d to
// the VDR object. Also means that the VDR object will not upload
// local changes, they will be discarded on Update().
// Protected by LockShared.
static cMutex LockShared;
// Common lock to access certain members from the VDR main thread
class cPrivateBase {
public:
cShadowBase *Parent;
// Parent is a pointer to the shadow object, or NULL if detached.
// This member is protected by LockShared mutex.
cPrivateBase(cShadowBase *parent);
// Constructors are always called with synced threads
virtual ~cPrivateBase();
// Warning: Higher level destructors need to call Parent->Detach().
// Foreground thread always calls destructor on its own
};
public:
virtual void Update()=0;
// Push object status to VDR object or create VDR object.
// Both threads MUST be synchronized before calling this
virtual void Detach()=0;
// Detach VDR object from this object. Can be called
// from any context.
virtual bool IsDetached()=0;
// Check if object is 'live'
cShadowBase();
virtual ~cShadowBase() {};
};
template<class T> class cShadowTemplate : public cShadowBase {
// VDR object shadow class
// This template encapsulates a VDR object as cOsdServerShadow<T>::cPrivate
// and provides the shadow cOsdServerObject for it.
// cPrivate lives in the VDR main thread, while cOsdServerShadow lives in the network thread.
// T needs to have a parameter-less constructor. Make one if you need one.
protected:
class cPrivate : public T, public cShadowBase::cPrivateBase {
public:
inline cPrivate(cShadowTemplate<T> *parent) : cShadowBase::cPrivateBase(parent) { };
friend class cShadowTemplate<T>;
};
cPrivate *Private;
// Pointer to the shadowed VDR object, or NULL if none attached (yet).
// Call Upade() to attach one.
public:
// Constructor and destructor are called from network thread.
// Warning: Higher level destructors need to call Detach().
cShadowTemplate<T>() {
Private=NULL;
}
virtual ~cShadowTemplate<T>() {
cMutexLock Lock(&LockShared);
if (Private) {
esyslog("osdserver: hard detaching of OSD shadow object");
Detach();
}
}
virtual void Detach() {
// This Detach() removes the cross-links between cOsdServerShadow and cPrivate.
// Detach private VDR object, make it independent
cMutexLock Lock(&LockShared);
if (!Private) return; // already detached
Private->Parent=NULL;
Private=NULL;
}
virtual bool IsDetached() { return Private==NULL; }
};
template<class T> class cShadowObjectTemplate : public cOsdServerObject, public cShadowTemplate<T> {
virtual bool EnableEvent(cEvent event) {
cMutexLock Lock(&cShadowBase::LockShared);
return cOsdServerObject::EnableEvent(event);
}
};
template<class T> class cShadowMenuItemTemplate : public cOsdServerMenuItem, public cShadowTemplate<T> {
public:
using cShadowTemplate<T>::Update;
using cShadowTemplate<T>::Detach;
using cShadowTemplate<T>::Private;
virtual void ItemUpdate() { Update(); }
virtual void ItemDetach() { Detach(); }
virtual cOsdItem* GetItem() { return Private; }
virtual bool EnableEvent(cEvent event) {
cMutexLock Lock(&cShadowBase::LockShared);
return cOsdServerObject::EnableEvent(event);
}
};
#endif
|