mirror of
https://github.com/awesomeWM/awesome
synced 2024-11-17 07:47:41 +01:00
33e209dd83
This kind-of-reverts 058dbab828
.
If banning_refresh() is called, only the lua events that it generated before are
now generated (the unfocus event). The actual mapping and unmapping of X11
windows is defered until the end of the main loop via a new per-screen
need_lazy_banning flag.
Signed-off-by: Uli Schlachter <psychon@znc.in>
Signed-off-by: Julien Danjou <julien@danjou.info>
496 lines
13 KiB
C
496 lines
13 KiB
C
/*
|
|
* tag.c - tag management
|
|
*
|
|
* Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
|
|
#include "screen.h"
|
|
#include "tag.h"
|
|
#include "client.h"
|
|
#include "ewmh.h"
|
|
#include "widget.h"
|
|
#include "luaa.h"
|
|
|
|
/** Tag type */
|
|
struct tag
|
|
{
|
|
LUA_OBJECT_HEADER
|
|
/** Tag name */
|
|
char *name;
|
|
/** Screen */
|
|
screen_t *screen;
|
|
/** true if selected */
|
|
bool selected;
|
|
/** clients in this tag */
|
|
client_array_t clients;
|
|
};
|
|
|
|
static lua_class_t tag_class;
|
|
LUA_OBJECT_FUNCS(tag_class, tag_t, tag)
|
|
|
|
|
|
void
|
|
tag_unref_simplified(tag_t **tag)
|
|
{
|
|
luaA_object_unref(globalconf.L, *tag);
|
|
}
|
|
|
|
/** Garbage collect a tag.
|
|
* \param L The Lua VM state.
|
|
* \return 0.
|
|
*/
|
|
static int
|
|
luaA_tag_gc(lua_State *L)
|
|
{
|
|
tag_t *tag = luaA_checkudata(L, 1, &tag_class);
|
|
client_array_wipe(&tag->clients);
|
|
p_delete(&tag->name);
|
|
return luaA_object_gc(L);
|
|
}
|
|
|
|
OBJECT_EXPORT_PROPERTY(tag, tag_t, selected)
|
|
OBJECT_EXPORT_PROPERTY(tag, tag_t, name)
|
|
|
|
/** View or unview a tag.
|
|
* \param L The Lua VM state.
|
|
* \param udx The index of the tag on the stack.
|
|
* \param view Set visible or not.
|
|
*/
|
|
static void
|
|
tag_view(lua_State *L, int udx, bool view)
|
|
{
|
|
tag_t *tag = luaA_checkudata(L, udx, &tag_class);
|
|
if(tag->selected != view)
|
|
{
|
|
tag->selected = view;
|
|
|
|
if(tag->screen)
|
|
{
|
|
int screen_index = screen_array_indexof(&globalconf.screens, tag->screen);
|
|
|
|
banning_need_update(tag->screen);
|
|
|
|
ewmh_update_net_current_desktop(screen_virttophys(screen_index));
|
|
|
|
if(globalconf.hooks.tags != LUA_REFNIL)
|
|
{
|
|
lua_pushnumber(globalconf.L, screen_index + 1);
|
|
luaA_object_push(globalconf.L, tag);
|
|
if(view)
|
|
lua_pushliteral(globalconf.L, "select");
|
|
else
|
|
lua_pushliteral(globalconf.L, "unselect");
|
|
luaA_dofunction_from_registry(globalconf.L, globalconf.hooks.tags, 3, 0);
|
|
}
|
|
}
|
|
|
|
luaA_object_emit_signal(L, udx, "property::selected", 0);
|
|
}
|
|
}
|
|
|
|
/** Append a tag to a screen.
|
|
* \param L The Lua VM state.
|
|
* \param udx The tag index on the stack.
|
|
* \param s The screen.
|
|
*/
|
|
void
|
|
tag_append_to_screen(lua_State *L, int udx, screen_t *s)
|
|
{
|
|
tag_t *tag = luaA_checkudata(globalconf.L, udx, &tag_class);
|
|
|
|
/* can't attach a tag twice */
|
|
if(tag->screen)
|
|
{
|
|
lua_remove(L, udx);
|
|
return;
|
|
}
|
|
|
|
int screen_index = screen_array_indexof(&globalconf.screens, s);
|
|
int phys_screen = screen_virttophys(screen_index);
|
|
|
|
tag->screen = s;
|
|
tag_array_append(&s->tags, luaA_object_ref_class(globalconf.L, udx, &tag_class));
|
|
ewmh_update_net_numbers_of_desktop(phys_screen);
|
|
ewmh_update_net_desktop_names(phys_screen);
|
|
ewmh_update_workarea(phys_screen);
|
|
|
|
luaA_object_push(globalconf.L, tag);
|
|
luaA_object_emit_signal(L, -1, "property::screen", 0);
|
|
lua_pop(L, 1);
|
|
|
|
/* call hook */
|
|
if(globalconf.hooks.tags != LUA_REFNIL)
|
|
{
|
|
lua_pushnumber(globalconf.L, screen_index + 1);
|
|
luaA_object_push(globalconf.L, tag);
|
|
lua_pushliteral(globalconf.L, "add");
|
|
luaA_dofunction_from_registry(globalconf.L, globalconf.hooks.tags, 3, 0);
|
|
}
|
|
|
|
luaA_object_push(globalconf.L, tag);
|
|
screen_emit_signal(globalconf.L, s, "tag::attach", 1);
|
|
}
|
|
|
|
/** Remove a tag from screen. Tag must be on a screen and have no clients.
|
|
* \param tag The tag to remove.
|
|
*/
|
|
void
|
|
tag_remove_from_screen(tag_t *tag)
|
|
{
|
|
if(!tag->screen)
|
|
return;
|
|
|
|
int screen_index = screen_array_indexof(&globalconf.screens, tag->screen);
|
|
int phys_screen = screen_virttophys(screen_index);
|
|
tag_array_t *tags = &tag->screen->tags;
|
|
|
|
for(int i = 0; i < tags->len; i++)
|
|
if(tags->tab[i] == tag)
|
|
{
|
|
tag_array_take(tags, i);
|
|
break;
|
|
}
|
|
|
|
/* tag was selected? If so, reban */
|
|
if(tag->selected)
|
|
banning_need_update(tag->screen);
|
|
|
|
ewmh_update_net_numbers_of_desktop(phys_screen);
|
|
ewmh_update_net_desktop_names(phys_screen);
|
|
ewmh_update_workarea(phys_screen);
|
|
|
|
/* call hook */
|
|
if(globalconf.hooks.tags != LUA_REFNIL)
|
|
{
|
|
lua_pushnumber(globalconf.L, screen_index + 1);
|
|
luaA_object_push(globalconf.L, tag);
|
|
lua_pushliteral(globalconf.L, "remove");
|
|
luaA_dofunction_from_registry(globalconf.L, globalconf.hooks.tags, 3, 0);
|
|
}
|
|
|
|
screen_t *s = tag->screen;
|
|
tag->screen = NULL;
|
|
|
|
luaA_object_push(globalconf.L, tag);
|
|
luaA_object_emit_signal(globalconf.L, -1, "property::screen", 0);
|
|
screen_emit_signal(globalconf.L, s, "tag::detach", 1);
|
|
|
|
luaA_object_unref(globalconf.L, tag);
|
|
}
|
|
|
|
static void
|
|
tag_client_emit_signal(lua_State *L, tag_t *t, client_t *c, const char *signame)
|
|
{
|
|
luaA_object_push(L, c);
|
|
luaA_object_push(L, t);
|
|
/* emit signal on client, with new tag as argument */
|
|
luaA_object_emit_signal(L, -2, signame, 1);
|
|
/* re push tag */
|
|
luaA_object_push(L, t);
|
|
/* move tag before client */
|
|
lua_insert(L, -2);
|
|
luaA_object_emit_signal(L, -2, signame, 1);
|
|
/* Remove tag */
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
/** Tag a client with the tag on top of the stack.
|
|
* \param c the client to tag
|
|
*/
|
|
void
|
|
tag_client(client_t *c)
|
|
{
|
|
tag_t *t = luaA_object_ref_class(globalconf.L, -1, &tag_class);
|
|
|
|
/* don't tag twice */
|
|
if(is_client_tagged(c, t))
|
|
{
|
|
luaA_object_unref(globalconf.L, t);
|
|
return;
|
|
}
|
|
|
|
client_array_append(&t->clients, c);
|
|
ewmh_client_update_desktop(c);
|
|
banning_need_update((c)->screen);
|
|
|
|
/* call hook */
|
|
if(globalconf.hooks.tagged != LUA_REFNIL)
|
|
{
|
|
luaA_object_push(globalconf.L, c);
|
|
luaA_object_push(globalconf.L, t);
|
|
luaA_dofunction_from_registry(globalconf.L, globalconf.hooks.tagged, 2, 0);
|
|
}
|
|
tag_client_emit_signal(globalconf.L, t, c, "tagged");
|
|
}
|
|
|
|
/** Untag a client with specified tag.
|
|
* \param c the client to tag
|
|
* \param t the tag to tag the client with
|
|
*/
|
|
void
|
|
untag_client(client_t *c, tag_t *t)
|
|
{
|
|
for(int i = 0; i < t->clients.len; i++)
|
|
if(t->clients.tab[i] == c)
|
|
{
|
|
client_array_take(&t->clients, i);
|
|
banning_need_update((c)->screen);
|
|
ewmh_client_update_desktop(c);
|
|
/* call hook */
|
|
if(globalconf.hooks.tagged != LUA_REFNIL)
|
|
{
|
|
luaA_object_push(globalconf.L, c);
|
|
luaA_object_push(globalconf.L, t);
|
|
luaA_dofunction_from_registry(globalconf.L, globalconf.hooks.tagged, 2, 0);
|
|
}
|
|
tag_client_emit_signal(globalconf.L, t, c, "untagged");
|
|
luaA_object_unref(globalconf.L, t);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/** Check if a client is tagged with the specified tag.
|
|
* \param c the client
|
|
* \param t the tag
|
|
* \return true if the client is tagged with the tag, false otherwise.
|
|
*/
|
|
bool
|
|
is_client_tagged(client_t *c, tag_t *t)
|
|
{
|
|
for(int i = 0; i < t->clients.len; i++)
|
|
if(t->clients.tab[i] == c)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Get the index of the first selected tag.
|
|
* \param screen Screen.
|
|
* \return Its index.
|
|
*/
|
|
int
|
|
tags_get_first_selected_index(screen_t *screen)
|
|
{
|
|
foreach(tag, screen->tags)
|
|
if((*tag)->selected)
|
|
return tag_array_indexof(&screen->tags, tag);
|
|
return 0;
|
|
}
|
|
|
|
/** Set a tag to be the only one viewed.
|
|
* \param target the tag to see
|
|
*/
|
|
static void
|
|
tag_view_only(tag_t *target)
|
|
{
|
|
if(target)
|
|
foreach(tag, target->screen->tags)
|
|
{
|
|
luaA_object_push(globalconf.L, *tag);
|
|
tag_view(globalconf.L, -1, *tag == target);
|
|
lua_pop(globalconf.L, 1);
|
|
}
|
|
}
|
|
|
|
/** View only a tag, selected by its index.
|
|
* \param screen Screen.
|
|
* \param dindex The index.
|
|
*/
|
|
void
|
|
tag_view_only_byindex(screen_t *screen, int dindex)
|
|
{
|
|
tag_array_t *tags = &screen->tags;
|
|
|
|
if(dindex < 0 || dindex >= tags->len)
|
|
return;
|
|
tag_view_only(tags->tab[dindex]);
|
|
}
|
|
|
|
/** Create a new tag.
|
|
* \param L The Lua VM state.
|
|
* \luastack
|
|
* \lparam A name.
|
|
* \lreturn A new tag object.
|
|
*/
|
|
static int
|
|
luaA_tag_new(lua_State *L)
|
|
{
|
|
if(lua_isstring(L, 2))
|
|
{
|
|
/* compat code */
|
|
luaA_deprecate(L, "new syntax");
|
|
|
|
size_t len;
|
|
const char *name = luaL_checklstring(L, 2, &len);
|
|
tag_t *tag = tag_new(globalconf.L);
|
|
|
|
a_iso2utf8(name, len, &tag->name, NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return luaA_class_new(L, &tag_class);
|
|
}
|
|
|
|
/** Get or set the clients attached to this tag.
|
|
* \param L The Lua VM state.
|
|
* \return The number of elements pushed on stack.
|
|
* \luastack
|
|
* \lparam None or a table of clients to set.
|
|
* \lreturn A table with the clients attached to this tags.
|
|
*/
|
|
static int
|
|
luaA_tag_clients(lua_State *L)
|
|
{
|
|
tag_t *tag = luaA_checkudata(L, 1, &tag_class);
|
|
client_array_t *clients = &tag->clients;
|
|
int i;
|
|
|
|
if(lua_gettop(L) == 2)
|
|
{
|
|
luaA_checktable(L, 2);
|
|
foreach(c, tag->clients)
|
|
untag_client(*c, tag);
|
|
lua_pushnil(L);
|
|
while(lua_next(L, 2))
|
|
{
|
|
client_t *c = luaA_client_checkudata(L, -1);
|
|
/* push tag on top of the stack */
|
|
lua_pushvalue(L, 1);
|
|
tag_client(c);
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
lua_createtable(L, clients->len, 0);
|
|
for(i = 0; i < clients->len; i++)
|
|
{
|
|
luaA_object_push(L, clients->tab[i]);
|
|
lua_rawseti(L, -2, i + 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(tag, tag_t, name, lua_pushstring)
|
|
LUA_OBJECT_EXPORT_PROPERTY(tag, tag_t, selected, lua_pushboolean)
|
|
|
|
/** Set the tag name.
|
|
* \param L The Lua VM state.
|
|
* \param tag The tag to name.
|
|
* \return The number of elements pushed on stack.
|
|
*/
|
|
static int
|
|
luaA_tag_set_name(lua_State *L, tag_t *tag)
|
|
{
|
|
size_t len;
|
|
const char *buf = luaL_checklstring(L, -1, &len);
|
|
p_delete(&tag->name);
|
|
a_iso2utf8(buf, len, &tag->name, NULL);
|
|
luaA_object_emit_signal(L, -3, "property::name", 0);
|
|
return 0;
|
|
}
|
|
|
|
/** Set the tag selection status.
|
|
* \param L The Lua VM state.
|
|
* \param tag The tag to set the selection status for.
|
|
* \return The number of elements pushed on stack.
|
|
*/
|
|
static int
|
|
luaA_tag_set_selected(lua_State *L, tag_t *tag)
|
|
{
|
|
tag_view(L, -3, luaA_checkboolean(L, -1));
|
|
return 0;
|
|
}
|
|
|
|
/** Set the tag screen.
|
|
* \param L The Lua VM state.
|
|
* \param tag The tag to set the screen for.
|
|
* \return The number of elements pushed on stack.
|
|
*/
|
|
static int
|
|
luaA_tag_set_screen(lua_State *L, tag_t *tag)
|
|
{
|
|
int screen;
|
|
if(lua_isnil(L, -1))
|
|
screen = -1;
|
|
else
|
|
{
|
|
screen = luaL_checknumber(L, -1) - 1;
|
|
luaA_checkscreen(screen);
|
|
}
|
|
|
|
tag_remove_from_screen(tag);
|
|
|
|
if(screen != -1)
|
|
tag_append_to_screen(L, -3, &globalconf.screens.tab[screen]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Get the tag screen.
|
|
* \param L The Lua VM state.
|
|
* \param tag The tag to get the screen for.
|
|
* \return The number of elements pushed on stack.
|
|
*/
|
|
static int
|
|
luaA_tag_get_screen(lua_State *L, tag_t *tag)
|
|
{
|
|
if(!tag->screen)
|
|
return 0;
|
|
lua_pushnumber(L, screen_array_indexof(&globalconf.screens, tag->screen) + 1);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
tag_class_setup(lua_State *L)
|
|
{
|
|
static const struct luaL_reg tag_methods[] =
|
|
{
|
|
LUA_CLASS_METHODS(tag)
|
|
{ "__call", luaA_tag_new },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
static const struct luaL_reg tag_meta[] =
|
|
{
|
|
LUA_OBJECT_META(tag)
|
|
LUA_CLASS_META
|
|
{ "clients", luaA_tag_clients },
|
|
{ "__gc", luaA_tag_gc },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
luaA_class_setup(L, &tag_class, "tag", (lua_class_allocator_t) tag_new,
|
|
luaA_class_index_miss_property, luaA_class_newindex_miss_property,
|
|
tag_methods, tag_meta);
|
|
luaA_class_add_property(&tag_class, A_TK_NAME,
|
|
(lua_class_propfunc_t) luaA_tag_set_name,
|
|
(lua_class_propfunc_t) luaA_tag_get_name,
|
|
(lua_class_propfunc_t) luaA_tag_set_name);
|
|
luaA_class_add_property(&tag_class, A_TK_SCREEN,
|
|
(lua_class_propfunc_t) NULL,
|
|
(lua_class_propfunc_t) luaA_tag_get_screen,
|
|
(lua_class_propfunc_t) luaA_tag_set_screen);
|
|
luaA_class_add_property(&tag_class, A_TK_SELECTED,
|
|
(lua_class_propfunc_t) luaA_tag_set_selected,
|
|
(lua_class_propfunc_t) luaA_tag_get_selected,
|
|
(lua_class_propfunc_t) luaA_tag_set_selected);
|
|
}
|
|
|
|
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|