示例#1
0
epicsShareFunc epicsThreadPool* epicsThreadPoolGetShared(epicsThreadPoolConfig *opts)
{
    ELLNODE *node;
    epicsThreadPool *cur;
    epicsThreadPoolConfig defopts;
    size_t N = epicsThreadGetCPUs();

    if (!opts) {
        epicsThreadPoolConfigDefaults(&defopts);
        opts = &defopts;
    }
    /* shared pools must have a minimum allowed number of workers.
     * Use the number of CPU cores
     */
    if (opts->maxThreads < N)
        opts->maxThreads = N;

    epicsThreadOnce(&sharedPoolsOnce, &sharedPoolsInit, NULL);

    epicsMutexMustLock(sharedPoolsGuard);

    for (node = ellFirst(&sharedPools); node; node = ellNext(node)) {
        cur = CONTAINER(node, epicsThreadPool, sharedNode);

        /* Must have exactly the requested priority
         * At least the requested max workers
         * and at least the requested stack size
         */
        if (cur->conf.workerPriority != opts->workerPriority)
            continue;
        if (cur->conf.maxThreads < opts->maxThreads)
            continue;
        if (cur->conf.workerStack < opts->workerStack)
            continue;

        cur->sharedCount++;
        assert(cur->sharedCount > 0);
        epicsMutexUnlock(sharedPoolsGuard);

        epicsMutexMustLock(cur->guard);
        *opts = cur->conf;
        epicsMutexUnlock(cur->guard);
        return cur;
    }

    cur = epicsThreadPoolCreate(opts);
    if (!cur) {
        epicsMutexUnlock(sharedPoolsGuard);
        return NULL;
    }
    cur->sharedCount = 1;

    ellAdd(&sharedPools, &cur->sharedNode);
    epicsMutexUnlock(sharedPoolsGuard);
    return cur;
}
示例#2
0
/**
 * Calculate the combined advisory minimum size of all components
 * 
 * @return  Advisory minimum size for the container
 */
static size2_t minimum_size(__this__)
{ 
  itk_component** children = CONTAINER(this)->children;
  long i, n = CONTAINER(this)->children_count;
  size2_t rc;
  rc.width = rc.height = 0;
  rc.defined = true;
  for (i = 0; i < n; i++)
    if ((*(children + i))->visible)
      {
	if (rc.width < (*(children + i))->minimum_size.width)
	  rc.width = (*(children + i))->minimum_size.width;
	if (rc.height < (*(children + i))->minimum_size.height)
	  rc.height = (*(children + i))->minimum_size.height;
      }
  rc.width += LEFT(this) + RIGHT(this);
  rc.height += TOP(this) + BOTTOM(this);
  return rc;
}
示例#3
0
文件: types.c 项目: man-at-work/ucl
static void* INTEGER32_data (uclptr_t exemplair)
{
	if (sizeof(int32_t) != sizeof(uclptr_t))
	{
		uclptr_t storage_index = (exemplair);
		//return (void*)*(intptr_t**)&uclmem[storage_index];
		return (void*)*(intptr_t**)&CONTAINER(storage_index);
	}
	else return (void*)(intptr_t)(exemplair);
}
示例#4
0
void dbStateShowAll(unsigned int level)
{
    ELLNODE *node;
    dbStateId id;

    for (node = ellFirst(&states); node; node = ellNext(node)) {
        id = CONTAINER(node, dbState, node);
        dbStateShow(id, level+1);
    }
}
示例#5
0
db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn) {
    chFilter *filter;
    ELLNODE *node;
    db_field_log *pLog = pLogIn;

    for (node = ellFirst(&chan->post_chain); node && pLog; node = ellNext(node)) {
        filter = CONTAINER(node, chFilter, post_node);
        pLog = filter->post_func(filter->post_arg, chan, pLog);
    }
    return pLog;
}
示例#6
0
static void createAndOpen(const char *chan, const char *json, const char *type, dbChannel**pch, short no) {
    ELLNODE *node;
    char name[80];

    strncpy(name, chan, sizeof(name)-1);
    strncat(name, json, sizeof(name)-strlen(name)-1);

    testOk(!!(*pch = dbChannelCreate(name)), "dbChannel with plugin arr %s created", type);
    testOk((ellCount(&(*pch)->filters) == no), "channel has %d filter(s) in filter list", no);

    testOk(!(dbChannelOpen(*pch)), "dbChannel with plugin arr opened");

    node = ellFirst(&(*pch)->pre_chain);
    (void) CONTAINER(node, chFilter, pre_node);
    testOk((ellCount(&(*pch)->pre_chain) == 0), "arr has no filter in pre chain");

    node = ellFirst(&(*pch)->post_chain);
    (void) CONTAINER(node, chFilter, post_node);
    testOk((ellCount(&(*pch)->post_chain) == no),
           "arr has %d filter(s) in post chain", no);
}
示例#7
0
dbStateId dbStateFind(const char *name)
{
    ELLNODE *node;
    dbStateId id;

    for (node = ellFirst(&states); node; node = ellNext(node)) {
        id = CONTAINER(node, dbState, node);
        if (strcmp(id->name, name) == 0)
            return id;
    }
    return NULL;
}
示例#8
0
static
int linuxDevFinal(void)
{
    ELLNODE *cur, *next, *isrcur, *isrnext;
    osdPCIDevice *curdev=NULL;
    osdISR *isr;

    epicsMutexMustLock(pciLock);
    for(cur=ellFirst(&devices), next=cur ? ellNext(cur) : NULL;
        cur;
        cur=next, next=next ? ellNext(next) : NULL )
    {
        curdev=CONTAINER(cur,osdPCIDevice,node);

        epicsMutexMustLock(curdev->devLock);

        for(isrcur=ellFirst(&curdev->isrs), isrnext=isrcur ? ellNext(isrcur) : NULL;
            isrcur;
            isrcur=isrnext, isrnext=isrnext ? ellNext(isrnext) : NULL )
        {
            isr=CONTAINER(isrcur,osdISR,node);

            stopIsrThread(isr);

            ellDelete(&curdev->isrs,cur);
            free(isr);

        }

        close_uio(curdev);

        epicsMutexUnlock(curdev->devLock);
        epicsMutexDestroy(curdev->devLock);
        free(curdev);
    }
    epicsMutexUnlock(pciLock);
    epicsMutexDestroy(pciLock);

    return 0;
}
示例#9
0
/**
 * Calculate the combined advisory maximum size of all components
 * 
 * @return  Advisory maximum size for the container
 */
static size2_t maximum_size(__this__)
{
  itk_component** children = CONTAINER(this)->children;
  long i, n = CONTAINER(this)->children_count;
  size2_t rc, t;
  rc.width = rc.height = UNBOUNDED;
  rc.defined = true;
  for (i = 0; i < n; i++)
    if ((*(children + i))->visible)
      {
	t = (*(children + i))->maximum_size;
	if (((rc.width < 0) || (rc.width > t.width)) && (t.width >= 0))
	  rc.width = t.width;
	if (((rc.height < 0) || (rc.height > t.height)) && (t.height >= 0))
	  rc.height = t.height;
      }
  if (rc.width >= 0)
    rc.width += LEFT(this) + RIGHT(this);
  if (rc.height >= 0)
    rc.height += TOP(this) + BOTTOM(this);
  return rc;
}
示例#10
0
bufRxManager::~bufRxManager()
{
    ELLNODE *node, *next;

    SCOPED_LOCK(guard);

    for(node=ellFirst(&freebufs), next=node ? ellNext(node):NULL;
        node;
        node=next, next=next ? ellNext(next) : NULL)
    {
        buffer *b=CONTAINER(node, buffer, node);
        free(b);
    }
}
示例#11
0
文件: types.c 项目: man-at-work/ucl
static void* VECTOR_data (uclptr_t exemplair) /* возвращает указатель на дескриптор массива индексов */
{
	if (sizeof(char*) <= sizeof(cell_t))
	{
		uclptr_t storage_index = (exemplair);
		return (void*)*(char**)&CONTAINER(storage_index);
	}
	else
	{
		void *p;
		unchop (&p, sizeof(void*), exemplair);
		return p;
	}
}
示例#12
0
static
int
linuxDevPCIBarLen(
  const epicsPCIDevice* dev,
  unsigned int bar,
  epicsUInt32 *len
)
{
    osdPCIDevice *osd=CONTAINER(dev,osdPCIDevice,dev);

    epicsMutexMustLock(osd->devLock);
    *len=osd->len[bar];
    epicsMutexUnlock(osd->devLock);
    return 0;
}
示例#13
0
static int
linuxDevPCISwitchInterrupt(const epicsPCIDevice *dev, int level)
{
    osdPCIDevice *osd=CONTAINER((epicsPCIDevice*)dev,osdPCIDevice,dev);
    epicsInt32    irq_on = !level;
    int ret;

    epicsMutexMustLock(osd->devLock);
    ret = open_uio(osd);
    epicsMutexUnlock(osd->devLock);
    if(ret)
        return S_dev_badInit;

    return write(osd->fd, &irq_on, sizeof(irq_on)) < 0 ? errno : 0;
}
示例#14
0
文件: types.c 项目: man-at-work/ucl
static void* STRING_data (uclptr_t exemplair)
{
	if (sizeof(char*) <= sizeof(cell_t))
	{
		uclptr_t storage_index = (exemplair);
		//return (void*)*(char**)&uclmem[storage_index];
		return (void*)*(char**)&CONTAINER(storage_index);
	}
	else
	{
		void *p;
		unchop (&p, sizeof(void*), exemplair);
		return p;
	}
}
示例#15
0
文件: types.c 项目: man-at-work/ucl
static uclptr_t INTEGER32_create (const void* sample_data)
/* Теперь надо посмотреть, влезает ли 32-битное число в поле CDR.
 * Если не влезает, оно будет храниться в отдельной ячейке (сделаем смелое предположение о том, что туда-то уж оно влезет). */
{
	if (sizeof(int32_t) != sizeof(uclptr_t)) /* определяется на этапе компиляции */
	{
		uclptr_t storage_index = cell_alloc(); /* возьмём ещё одну ячейку */
		//int32_t **storage_cell_ptr = (int32_t**)&uclmem[storage_index]; /* представим, что это указатель на int */
		int32_t **storage_cell_ptr = (int32_t**)&CONTAINER(storage_index); /* представим, что это указатель на int */
		MANAGED(storage_index) = 1;
		*storage_cell_ptr = (int32_t*)sample_data; /* положим его туда */
		return storage_index; /* вернём индекс ячейки хранения */
	}
	else return (uint32_t)((intptr_t)sample_data);
}
示例#16
0
/**
 * @brief Returns true if actor can reload weapon
 * @sa AI_ActorThink
 */
bool G_ClientCanReload (edict_t *ent, containerIndex_t containerID)
{
	invList_t *ic;
	containerIndex_t container;
	const objDef_t *weapon;

	if (CONTAINER(ent, containerID)) {
		weapon = CONTAINER(ent, containerID)->item.item;
	} else if (containerID == gi.csi->idLeft && RIGHT(ent)->item.item->holdTwoHanded) {
		/* Check for two-handed weapon */
		containerID = gi.csi->idRight;
		weapon = CONTAINER(ent, containerID)->item.item;
	} else
		return false;

	assert(weapon);

	/* also try the temp containers */
	for (container = 0; container < gi.csi->numIDs; container++)
		for (ic = CONTAINER(ent, container); ic; ic = ic->next)
			if (INVSH_LoadableInWeapon(ic->item.item, weapon))
				return true;
	return false;
}
示例#17
0
void
bufRxManager::dataRxDeleteReceive(dataBufComplete fn,void* arg)
{
    listener *l;

    SCOPED_LOCK(guard);

    for(ELLNODE *node=ellFirst(&dispatch); node; node=ellNext(node))
    {
        l=CONTAINER(node, listener, node);
        if (l->fn==fn && l->fnarg==arg) {
            ellDelete(&dispatch, node);
            delete l;
            return;
        }
    }
}
示例#18
0
void Grid_vdestroy(void *vthis) {
#ifdef VERBOSE_CREATE
	Static_printObj(vthis);
#endif
	/*! Delete self stuff !*/
	if (GRID(vthis)->items) {
		u16 i = 0; 
		for (; i < GRID(vthis)->c_row; i++) free(GRID(vthis)->items[i]);
		free(GRID(vthis)->items);
		GRID(vthis)->items = NULL;
		GRID(vthis)->c_col = GRID(vthis)->c_row = 0;
	}
	free(GRID(vthis)->maxw); GRID(vthis)->maxw = NULL;
	
	/*! Delete parent object !*/
	Container_vdestroy(CONTAINER(vthis));
}
示例#19
0
文件: insert.hpp 项目: jamesg/sqlite
 void insert(
         std::string table,
         std::vector<boost::optional<const char*>> fields,
         json::list& values,
         connection& conn
         )
 {
     std::string query_str = detail::insert_query_string(table, fields);
     sqlite3_stmt *stmt = nullptr;
     sqlite3_prepare(conn.handle(), query_str.c_str(), -1, &stmt, nullptr);
     for(json::object& obj : values.objects)
     {
         sqlite3_reset(stmt);
         sqlite3_clear_bindings(stmt);
         bind_values(fields, CONTAINER(obj), stmt);
         sqlite::step(stmt);
     }
 }
示例#20
0
/**
 * @brief Removes one particular item from a given container
 * @param itemID The id of the item to remove
 * @param ent The edict that holds the inventory to remove the item from
 * @param container The container in the inventory of the edict to remove the searched item from.
 * @return @c true if the removal was successful, @c false otherwise.
 */
bool G_InventoryRemoveItemByID (const char *itemID, edict_t *ent, containerIndex_t container)
{
	invList_t *ic = CONTAINER(ent, container);
	while (ic) {
		const objDef_t *item = ic->item.item;
		if (item != NULL && Q_streq(item->id, itemID)) {
			/* remove the virtual item to update the inventory lists */
			if (!game.i.RemoveFromInventory(&game.i, &ent->chr.i, INVDEF(container), ic))
				gi.Error("Could not remove item '%s' from inventory %i",
						ic->item.item->id, container);
			G_EventInventoryDelete(ent, G_VisToPM(ent->visflags), INVDEF(container), ic->x, ic->y);
			return true;
		}
		ic = ic->next;
	}

	return false;
}
示例#21
0
epicsUInt8*
bufRxManager::getFree(unsigned int* blen)
{
    ELLNODE *node;
    {
        SCOPED_LOCK(guard);
        node=ellGet(&freebufs);
    }

    if (!node)
        return NULL;

    buffer *buf=CONTAINER(node, buffer, node);

    if (blen) *blen=bsize();

    buf->used=0;
    return buf->data;
}
示例#22
0
/**
 * @brief Retrieve or collect weapon from any linked container for the actor
 * @note This function will also collect items from floor containers when the actor
 * is standing on a given point.
 * @sa AI_ActorThink
 */
void G_ClientGetWeaponFromInventory (edict_t *ent)
{
	invList_t *ic;
	invList_t *icFinal;
	const invDef_t *invDef;
	int tu;
	containerIndex_t container;
	const invDef_t *bestContainer;

	/* e.g. bloodspiders are not allowed to carry or collect weapons */
	if (!ent->chr.teamDef->weapons)
		return;

	/* search for weapons and select the one that is available easily */
	tu = 100;
	invDef = INVDEF(gi.csi->idRight);
	bestContainer = NULL;
	icFinal = NULL;

	/* also try the temp containers */
	for (container = 0; container < gi.csi->numIDs; container++) {
		if (INVDEF(container)->out < tu) {
			/* Once we've found at least one clip, there's no point
			 * searching other containers if it would take longer
			 * to retrieve the ammo from them than the one
			 * we've already found. */
			for (ic = CONTAINER(ent, container); ic; ic = ic->next) {
				assert(ic->item.item);
				if (ic->item.item->weapon && (ic->item.ammoLeft > 0 || !ic->item.item->reload)) {
					icFinal = ic;
					bestContainer = INVDEF(container);
					tu = bestContainer->out;
					break;
				}
			}
		}
	}

	/* send request */
	if (bestContainer)
		G_ActorInvMove(ent, bestContainer, icFinal, invDef, 0, 0, true);
}
示例#23
0
static
ssize_t stream_write(h2stream* S, char *buf, size_t blen)
{
    char sbuf[10];
    request *req = CONTAINER(S, request, R);

    if(!req->cansend) return NGHTTP2_ERR_DEFERRED;
    req->cansend = 0;

    sprintf(sbuf, "%08x\n", req->count++);

    size_t L = strlen(sbuf);
    if(L>blen) {
        fprintf(stderr, "Counter truncated line\n");
        L = blen;
    }
    memcpy(buf, sbuf, L);
    printf("Server sent %lu bytes\n", (unsigned long)L);
    return L;
}
示例#24
0
文件: types.c 项目: man-at-work/ucl
static uclptr_t VECTOR_create (const void* sample_data) /* параметр - uint32_t, количество выделяемых ячеек. */
/* Эта функция только создаёт пустой вектор, его наполнение - забота совершенно другой конструкции.
 * */
{
	uint32_t i, n = (uintptr_t)sample_data;
	uint32_t size = (n * sizeof(uclptr_t));	/* смело! А вдруг упрёмся в ограничение? */
	ucl_vector_t *vector;
	uclptr_t storage_index;

	if (sizeof(void*) <= sizeof(cell_t)) /* если указатель на кучу влезает целиком в поле container, то используем эту схему хранения */
	{
		storage_index = cell_alloc();
		if (storage_index != NIL)
		{
			ucl_vector_t **storage_cell_ptr = (ucl_vector_t**)&CONTAINER(storage_index);
			//GC(storage_index) = GC_TRANSIENT;
			MANAGED(storage_index) = 1;
			*storage_cell_ptr = (ucl_vector_t*)malloc(sizeof(ucl_vector_t)); /* выделяем память под дескриптор ветора */
			vector = *storage_cell_ptr;
		}
		else
		{
			printf ("Panic stop: no free memory left!\n");
			exit(-1);
		}
	}
	else /* если физический указатель очень велик и не помещается даже на место целой ячейки, тогда используем другую схему хранения - чоппер! */
	{
		vector = (ucl_vector_t*)malloc(sizeof(ucl_vector_t));
		storage_index = chop (&vector, sizeof(ucl_vector_t*));
	}
	vector->length = n;
	vector->ptr = (uclptr_t*)malloc(size);
	for (i=0; i < vector->length; i++)
	{
		vector->ptr[i] = NIL;
	}
	return storage_index;
}
示例#25
0
void
bufRxManager::dataRxAddReceive(dataBufComplete fn,void* arg)
{
    listener *l;

    SCOPED_LOCK(guard);

    for(ELLNODE *node=ellFirst(&dispatch); node; node=ellNext(node))
    {
        l=CONTAINER(node, listener, node);
        // Don't add duplicates
        if (l->fn==fn && l->fnarg==arg) {
            return;
        }
    }

    l=new listener;
    l->fn=fn;
    l->fnarg=arg;

    ellAdd(&dispatch, &l->node);
}
示例#26
0
/**
 * Locates the positions of the corners of a component
 * 
 * @param   child  The child, to the component using the layout manager, of interest
 * @return         The rectangle the child is confound in
 */
static rectangle_t locate(__this__, itk_component* child)
{
  if (child->visible)
    {
      size2_t c = CONTAINER(this)->size;
      position_t x = LEFT(this), y = TOP(this);
      dimension_t w = c.width - RIGHT(this);
      dimension_t h = c.height - BOTTOM(this);
      if ((w | h) < 0)
	{
	  w = c.width;
	  h = c.height;
	  x = y = 0;
	}
      return new_rectangle(x, y, w, h);
    }
  else
    {
      rectangle_t rc;
      rc.defined = false;
      return rc;
    }
}
示例#27
0
epicsShareFunc
int
devLibPCIRegisterDriver2(devLibPCI* drv, size_t drvsize)
{
    int ret=0;
    ELLNODE *cur;

    if (!drv->name) return 1;

    if(drvsize!=sizeof(*drv)) {
        errlogPrintf("devLibPCIRegisterDriver() fails with inconsistent PCI OS struct sizes.\n"
                     "expect %lu but given %lu\n"
                     "Please do a clean rebuild of devLib2 and any code with custom PCI OS structs\n",
                     (unsigned long)sizeof(*drv),
                     (unsigned long)drvsize);
        return S_dev_internal;
    }

    epicsThreadOnce(&devPCIReg_once, &regInit, NULL);

    epicsMutexMustLock(pciDriversLock);

    for(cur=ellFirst(&pciDrivers); cur; cur=ellNext(cur)) {
        devLibPCI *other=CONTAINER(cur, devLibPCI, node);
        if (strcmp(drv->name, other->name)==0) {
            errlogPrintf("Failed to register PCI bus driver: name already taken\n");
            ret=1;
            break;
        }
    }
    if (!ret)
        ellAdd(&pciDrivers, &drv->node);

    epicsMutexUnlock(pciDriversLock);

    return ret;
}
示例#28
0
static void handle_close(gTabStrip *sender, int index)
{
	CWIDGET *_object = GetObject(sender);
	GB.Raise(THIS, EVENT_Close, 1, GB_T_INTEGER, index);
}

/***************************************************************************

	TabStrip

***************************************************************************/

BEGIN_METHOD(CTABSTRIP_new, GB_OBJECT parent)

	InitControl(new gTabStrip(CONTAINER(VARG(parent))), (CWIDGET*)THIS);
	TABSTRIP->onClick = gb_tabstrip_post_click;
	TABSTRIP->onClose = handle_close;
	gb_tabstrip_post_click(TABSTRIP);

END_METHOD


BEGIN_METHOD_VOID(TabStrip_free)

	GB.Unref(POINTER(&THIS->textFont));

END_METHOD


BEGIN_PROPERTY(CTABSTRIP_tabs)
示例#29
0
static
void workerMain(void *arg)
{
    epicsThreadPool *pool = arg;
    unsigned int nrun, ocnt;

    /* workers are created with counts
     * in the running, sleeping, and (possibly) waking counters
     */

    epicsMutexMustLock(pool->guard);
    pool->threadsAreAwake++;
    pool->threadsSleeping--;

    while (1) {
        ELLNODE *cur;

        pool->threadsAreAwake--;
        pool->threadsSleeping++;
        epicsMutexUnlock(pool->guard);

        epicsEventMustWait(pool->workerWakeup);

        epicsMutexMustLock(pool->guard);
        pool->threadsSleeping--;
        pool->threadsAreAwake++;

        if (pool->threadsWaking==0)
            continue;

        pool->threadsWaking--;

        CHECKCOUNT(pool);

        if (pool->shutdown)
            break;

        if (pool->pauserun)
            continue;

        /* more threads to wakeup */
        if (pool->threadsWaking) {
            epicsEventSignal(pool->workerWakeup);
        }

        while ((cur=ellGet(&pool->jobs)) != NULL) {
            epicsJob *job = CONTAINER(cur, epicsJob, jobnode);

            assert(job->queued && !job->running);

            job->queued=0;
            job->running=1;

            epicsMutexUnlock(pool->guard);
            (*job->func)(job->arg, epicsJobModeRun);
            epicsMutexMustLock(pool->guard);

            if (job->freewhendone) {
                job->dead=1;
                free(job);
            }
            else {
                job->running=0;
                /* job may be re-queued from within callback */
                if (job->queued)
                    ellAdd(&pool->jobs, &job->jobnode);
                else
                    ellAdd(&pool->owned, &job->jobnode);
            }
        }

        if (pool->observerCount)
            epicsEventSignal(pool->observerWakeup);
    }

    pool->threadsAreAwake--;
    pool->threadsRunning--;

    nrun = pool->threadsRunning;
    ocnt = pool->observerCount;
    epicsMutexUnlock(pool->guard);

    if (ocnt)
        epicsEventSignal(pool->observerWakeup);

    if (nrun)
        epicsEventSignal(pool->workerWakeup); /* pass along */
    else
        epicsEventSignal(pool->shutdownEvent);
}
示例#30
0
文件: menu.c 项目: atrinik/atrinik
/** Handles highlighting of menuitems when the cursor is hovering over them. */
void widget_highlight_menu(widgetdata *widget)
{
    widgetdata *tmp, *tmp2, *tmp3, *submenuwidget = NULL;
    _menu *menu = NULL, *tmp_menu = NULL;
    _menuitem *menuitem = NULL, *submenuitem = NULL;
    int visible, create_submenu = 0, x = 0, y = 0;

    /* Sanity check. Make sure widget is a menu. */
    if (!widget || widget->sub_type != MENU_ID) {
        return;
    }

    /* Check to see if the cursor is hovering over a menuitem or a widget inside
     * it.
     * We don't need to go recursive here, just scan the immediate children. */
    for (tmp = widget->inv; tmp; tmp = tmp->next) {
        visible = 0;

        /* Menuitem is being directly hovered over. Make the background visible
         * for visual feedback. */
        if (tmp == widget_mouse_event.owner) {
            visible = 1;
        } else if (tmp->inv) {
            /* The cursor is hovering over something the menuitem contains. This
             * only needs to search the direct children,
             * as there should be nothing contained within the children. */

            for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->next) {
                if (tmp2 == widget_mouse_event.owner) {
                    /* The cursor was hovering over something inside the
                     * menuitem. */
                    visible = 1;
                    break;
                }
            }
        }

        /* Only do any real working if the state of the menuitem changed. */
        if (tmp->hidden != visible) {
            continue;
        }

        menu = MENU(widget);
        menuitem = MENUITEM(tmp);

        /* Cursor has just started to hover over the menuitem. */
        if (visible) {
            tmp->hidden = 0;

            /* If the highlighted menuitem is a submenu, we need to create a
             * submenu next to the menuitem. */
            if (menuitem->menu_type == MENU_SUBMENU) {
                create_submenu = 1;
                submenuitem = menuitem;
                submenuwidget = tmp;
                x = tmp->x + widget->w - 4;
                y = tmp->y - (CONTAINER(widget))->outer_padding_top;
            }
        } else {
            /* Cursor no longer hovers over the menuitem. */

            tmp->hidden = 1;

            /* Let's check if we need to remove the submenu.
             * Don't remove it if the cursor is hovering over the submenu
             * itself,
             * or a submenu of the submenu, etc. */
            if (menuitem->menu_type == MENU_SUBMENU && menu->submenu) {
                /* This will for sure get the menu that the cursor is hovering
                 * over. */
                tmp2 = get_outermost_container(widget_mouse_event.owner);

                /* Just in case the 'for sure' part of the last comment turns
                 * out to be incorrect... */
                if (tmp2 && tmp2->sub_type == MENU_ID) {
                    /* Loop through the submenus to see if we find a match for
                     * the menu the cursor is hovering over. */
                    for (tmp_menu = menu; tmp_menu->submenu && tmp_menu->submenu != tmp2; tmp_menu = MENU(tmp_menu->submenu)) {
                    }

                    /* Remove any submenus related to menu if the menu
                     * underneath the cursor is not a submenu of menu. */
                    if (!tmp_menu->submenu) {
                        tmp2 = menu->submenu;

                        while (tmp2) {
                            tmp3 = (MENU(tmp2))->submenu;
                            remove_widget_object(tmp2);
                            tmp2 = tmp3;
                        }

                        menu->submenu = NULL;
                    } else {
                        /* Cursor is hovering over the submenu, so leave this
                         * menuitem highlighted. */
                        tmp->hidden = 0;
                    }
                } else {
                    /* Cursor is not over a menu, so leave the menuitem containing
                     * the submenu highlighted.
                     * We want to keep the submenu open, which should reduce
                     * annoyance if the user is not precise with the mouse. */
                    tmp->hidden = 0;
                }
            }
        }
    }

    /* If a submenu needs to be created, create it now. Make sure there can be
     * only one submenu open here. */
    if (create_submenu && !menu->submenu) {
        tmp_menu = MENU(widget);

        tmp_menu->submenu = create_menu(x, y, tmp_menu->owner);

        if (submenuitem->menu_func_ptr) {
            submenuitem->menu_func_ptr(tmp_menu->owner, submenuwidget, NULL);
        }

        menu_finalize(tmp_menu->submenu);
    }
}