← Back to team overview

ayatana-commits team mailing list archive

[Branch ~notify-osd-developers/notify-osd/main] Rev 389: Merged two-slots branch to trunk, sticking with term GRAVITY (used to be

 

Merge authors:
  Mirco Müller (macslow)
Related merge proposals:
  https://code.launchpad.net/~macslow/notify-osd/two-slots/+merge/11168
  proposed by: Mirco Müller (macslow)
  review: Approve - David Barth (dbarth)
------------------------------------------------------------
revno: 389 [merge]
committer: Mirco Müller <mirco.mueller@xxxxxxxxxx>
branch nick: notify-osd
timestamp: Tue 2009-09-08 15:26:40 +0200
message:
  Merged two-slots branch to trunk, sticking with term GRAVITY (used to be
  called PLACEMENT). GRAVITY is now a setting stored in class Defaults. It
  used to be living in class Stack, but didn't really belong there. This
  fixes LP: #371422
modified:
  src/bubble.c
  src/bubble.h
  src/display.c
  src/stack.c
  src/stack.h
  tests/test-stack.c


--
lp:notify-osd
https://code.launchpad.net/~notify-osd-developers/notify-osd/main

Your team ayatana-commits is subscribed to branch lp:notify-osd.
To unsubscribe from this branch go to https://code.launchpad.net/~notify-osd-developers/notify-osd/main/+edit-subscription.
=== modified file 'src/bubble.c'
--- src/bubble.c	2009-08-26 11:17:55 +0000
+++ src/bubble.c	2009-08-27 09:52:34 +0000
@@ -83,7 +83,6 @@
 	gboolean         icon_only;
 	gint             future_height;
 	gboolean         prevent_fade;
-	BubblePlacement  placement;
 
 	// these will be replaced by notification_t* later on
 	GString*         title;
@@ -2251,7 +2250,6 @@
 	this->priv->tile_body            = NULL;
 	this->priv->tile_indicator       = NULL;
 	this->priv->prevent_fade         = FALSE;
-	this->priv->placement		 = PLACEMENT_NEW;
 
 	update_input_shape (window, 1, 1);
 
@@ -3649,12 +3647,3 @@
 	bubble_start_timer (self);
 	bubble_start_timer (other);
 }
-
-BubblePlacement
-bubble_get_placement (Bubble* self)
-{
-	if (!self || !IS_BUBBLE (self))
-		return PLACEMENT_NONE;
-
-	return GET_PRIVATE (self)->placement;
-}

=== modified file 'src/bubble.h'
--- src/bubble.h	2009-08-26 09:35:02 +0000
+++ src/bubble.h	2009-08-27 09:52:34 +0000
@@ -48,13 +48,6 @@
 	LAYOUT_TITLE_ONLY
 } BubbleLayout;
 
-typedef enum
-{
-	PLACEMENT_NONE = 0,
-	PLACEMENT_OLD, // top-right of screen
-	PLACEMENT_NEW  // vertically centered at right of screen
-} BubblePlacement;
-
 G_BEGIN_DECLS
 
 #define BUBBLE_TYPE             (bubble_get_type ())
@@ -267,9 +260,6 @@
 		    const char *process_name,
 		    gchar **actions);
 
-BubblePlacement
-bubble_get_placement (Bubble* self);
-
 G_END_DECLS
 
 #endif // __BUBBLE_H

=== modified file 'src/display.c'
--- src/display.c	2009-08-31 13:56:28 +0000
+++ src/display.c	2009-09-08 13:26:40 +0000
@@ -71,14 +71,12 @@
 	return y1 == y2;
 }
 
-
 static void
 stack_display_position_sync_bubble (Stack *self, Bubble *bubble)
 {
 	Defaults* d = self->defaults;
 	gint      y = 0;
 	gint      x = 0;
-	Bubble*   async;
 
 	defaults_get_top_corner (d, &x, &y);
 
@@ -88,21 +86,103 @@
 	switch (defaults_get_gravity (d))
 	{
 		case GRAVITY_NORTH_EAST:
-			async = stack_find_bubble_on_display (self);
-			if (async != NULL)
-			{
-				d = self->defaults;
-				y += bubble_get_future_height (async);
-				y += EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
-				2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
+			// see if we're call at the wrong moment, when both
+			// slots are occupied by bubbles
+			if (!stack_is_slot_vacant (self, SLOT_TOP) &&
+			    !stack_is_slot_vacant (self, SLOT_BOTTOM))
+			{
+				g_warning ("%s(): Both slots taken!\n",
+				           G_STRFUNC);
+			}
+			else
+			{
+				// first check if we can place the sync. bubble
+				// in the top slot and the bottom slot is still
+				// vacant, this is to avoid the "gap" between
+				// bottom slot and panel
+				if (stack_is_slot_vacant (self, SLOT_TOP) &&
+				    stack_is_slot_vacant (self, SLOT_BOTTOM))
+				{
+					stack_get_slot_position (self,
+						                 SLOT_TOP,
+					                         bubble_get_height (bubble),
+						                 &x,
+						                 &y);
+					if (x == -1 || y == -1)
+						g_warning ("%s(): No coords!\n",
+							   G_STRFUNC);
+					else
+						stack_allocate_slot (self,
+							             bubble,
+							             SLOT_TOP);
+				}
+				// next check if top is occupied and bottom is
+				// still vacant, then place sync. bubble in
+				// bottom slot
+				else if (!stack_is_slot_vacant (self,
+				                                SLOT_TOP) &&
+				         stack_is_slot_vacant (self,
+				                               SLOT_BOTTOM))
+				{
+					stack_get_slot_position (self,
+						                 SLOT_BOTTOM,
+					                         bubble_get_height (bubble),
+						                 &x,
+						                 &y);
+					if (x == -1 || y == -1)
+						g_warning ("%s(): No coords!\n",
+							   G_STRFUNC);
+					else
+					{
+						stack_allocate_slot (
+							self,
+							bubble,
+							SLOT_BOTTOM);
+
+						bubble_sync_with (
+							bubble,
+							self->slots[SLOT_TOP]);
+					}
+				}
+				// this case, top vacant, bottom occupied,
+				// should never happen for the old placement,
+				// we want to avoid the "gap" between the bottom
+				// bubble and the panel
+				else if (stack_is_slot_vacant (self,
+				                               SLOT_TOP) &&
+				         !stack_is_slot_vacant (self,
+				                                SLOT_BOTTOM))
+				{
+					g_warning ("%s(): Gap, gap, gap!!!\n",
+					           G_STRFUNC);
+				}
 			}
 		break;
 
 		case GRAVITY_EAST:
-			y += defaults_get_desktop_height (d) / 2 -
-			     EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
-			     bubble_get_height (bubble) +
-			     EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
+			// see if reserved top slot for sync. bubble is really
+			// vacant
+			if (stack_is_slot_vacant (self, SLOT_TOP) == OCCUPIED)
+			{
+				g_warning ("%s(): Top slot taken!\n",
+				           G_STRFUNC);
+			}
+			// if not just put sync. bubble in top slot
+			else
+			{
+				stack_get_slot_position (self,
+				                         SLOT_TOP,
+				                         bubble_get_height (bubble),
+				                         &x,
+				                         &y);
+				if (x == -1 || y == -1)
+					g_warning ("%s(): No slot-coords!\n",
+					           G_STRFUNC);
+				else
+					stack_allocate_slot (self,
+					                     bubble,
+					                     SLOT_TOP);
+			}
 		break;
 
 		default:
@@ -114,6 +194,7 @@
 }
 
 static void
+
 stack_display_sync_bubble (Stack *self, Bubble *bubble)
 {
 	g_return_if_fail (IS_STACK (self));
@@ -244,33 +325,116 @@
 	switch (defaults_get_gravity (d))
 	{
 		case GRAVITY_NORTH_EAST:
-			if (sync_bubble != NULL && bubble_is_visible (sync_bubble))
-			{
-				d = self->defaults;
-				y += bubble_get_height (sync_bubble);
-				y += EM2PIXELS (defaults_get_bubble_vert_gap (d), d)
-				     - 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
+			if (!stack_is_slot_vacant (self, SLOT_TOP) &&
+			    !stack_is_slot_vacant (self, SLOT_BOTTOM))
+			{
+				g_warning ("%s(): Both slots taken!\n",
+				           G_STRFUNC);
+			}
+			else
+			{
+				if (stack_is_slot_vacant (self, SLOT_TOP))
+				{
+					stack_get_slot_position (self,
+						                 SLOT_TOP,
+					                         bubble_get_height (bubble),
+						                 &x,
+						                 &y);
+					if (x == -1 || y == -1)
+						g_warning ("%s(): No coords!\n",
+							   G_STRFUNC);
+					else
+						stack_allocate_slot (self,
+							             bubble,
+							             SLOT_TOP);
+				}
+				else if (stack_is_slot_vacant (self,
+				                               SLOT_BOTTOM))
+				{
+					stack_get_slot_position (self,
+						                 SLOT_BOTTOM,
+					                         bubble_get_height (bubble),
+						                 &x,
+						                 &y);
+					if (x == -1 || y == -1)
+						g_warning ("%s(): No coords!\n",
+							   G_STRFUNC);
+					else
+						stack_allocate_slot (self,
+							             bubble,
+							             SLOT_BOTTOM);
+				}
+			}
 
-				// synchronize the sync bubble with the timeout of the bubble at
-				// the bottom
-				if (stack_is_at_top_corner (self, sync_bubble))
-					bubble_sync_with (sync_bubble, bubble);
+			if (sync_bubble != NULL &&
+			    bubble_is_visible (sync_bubble) &&
+			    sync_bubble == self->slots[SLOT_TOP])
+			{
+				// synchronize the sync bubble with the timeout
+				// of the bubble at the bottom
+				bubble_sync_with (sync_bubble, bubble);
 			}
 		break;
 
 		case GRAVITY_EAST:
-			if (sync_bubble != NULL && bubble_is_visible (sync_bubble))
+			// with the new placement sync. bubbles are always to be
+			// placed in the top slot (above the "half-line")
+			if (bubble_is_synchronous (bubble))
 			{
-				y += defaults_get_desktop_height (d) / 2 -
-				     EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
-				     bubble_get_height (sync_bubble) +
-				     EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
+				// verify that the top slot is really vacant
+				if (stack_is_slot_vacant (self, SLOT_TOP))
+				{
+					stack_get_slot_position (self,
+						                 SLOT_TOP,
+					                         bubble_get_height (bubble),
+						                 &x,
+						                 &y);
+					if (x == -1 || y == -1)
+						g_warning ("%s(): No coords!\n",
+							   G_STRFUNC);
+					else
+						stack_allocate_slot (self,
+							             bubble,
+							             SLOT_TOP);
+				}
+				// otherwise there's still an error in the
+				// layout- and queue-logic
+				else
+				{
+					g_warning ("%s(): Can't put sync. "
+					           "bubble in top slot!\n",
+					           G_STRFUNC);
+				}
 			}
+			// an async. bubble is always meant to be put in the
+			// bottom slot (below the "half-line")
 			else
 			{
-				y += defaults_get_desktop_height (d) / 2 +
-				     EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
-				     EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
+				// verify that the bottom slot is really vacant
+				if (stack_is_slot_vacant (self, SLOT_BOTTOM))
+				{
+					stack_get_slot_position (self,
+						                 SLOT_BOTTOM,
+					                         bubble_get_height (bubble),
+						                 &x,
+						                 &y);
+					if (x == -1 || y == -1)
+						g_warning ("%s(): No coords!\n",
+							   G_STRFUNC);
+					else
+						stack_allocate_slot (
+							self,
+							bubble,
+							SLOT_BOTTOM);
+				}
+				// otherwise there's still an error in the
+				// layout- and queue-logic
+				else
+				{
+					g_warning ("%s(): Can't put async. "
+					           "bubble in bottom slot!\n",
+					           G_STRFUNC);
+				}
 			}
 		break;
 

=== modified file 'src/stack.c'
--- src/stack.c	2009-08-26 11:17:55 +0000
+++ src/stack.c	2009-09-08 13:26:40 +0000
@@ -319,10 +319,12 @@
 	if (!this)
 		return NULL;
 
-	this->defaults = defaults;
-	this->observer = observer;
-	this->list     = NULL;
-	this->next_id  = 1;
+	this->defaults           = defaults;
+	this->observer           = observer;
+	this->list               = NULL;
+	this->next_id            = 1;
+	this->slots[SLOT_TOP]    = NULL;
+	this->slots[SLOT_BOTTOM] = NULL;
 
 	/* hook up handler to act on changes of defaults/settings */
 	g_signal_connect (G_OBJECT (defaults),
@@ -354,6 +356,9 @@
 
 	if (n != NULL)
 	{
+		if (IS_BUBBLE (n))
+			stack_free_slot (stack, BUBBLE (n));
+
 		if (IS_BUBBLE (n)
 		    && bubble_is_synchronous (BUBBLE (n)))
 		{
@@ -736,7 +741,6 @@
 	if (bubble_is_synchronous (bubble))
 	{
 		stack_display_sync_bubble (self, bubble);
-
 	} else {
 		stack_push_bubble (self, bubble);
 
@@ -750,9 +754,10 @@
 		/* make sure the sync. bubble is positioned correctly
 		   even for the append case
 		*/
-		if (sync_bubble != NULL
-		    && bubble_is_visible (sync_bubble))
-			stack_display_position_sync_bubble (self, sync_bubble);
+		// no longer needed since we have the two-slots mechanism now
+		//if (sync_bubble != NULL
+		//    && bubble_is_visible (sync_bubble))
+		//	stack_display_position_sync_bubble (self, sync_bubble);
 
 		/* update the layout of the stack;
 		 * this will also open the new bubble */
@@ -829,3 +834,146 @@
 
 	return TRUE;
 }
+
+gboolean
+stack_is_slot_vacant (Stack* self,
+                      Slot   slot)
+{
+	// sanity checks
+	if (!self || !IS_STACK (self))
+		return FALSE;
+
+	if (slot != SLOT_TOP && slot != SLOT_BOTTOM)
+		return FALSE;
+
+	return self->slots[slot] == NULL ? VACANT : OCCUPIED;
+}
+
+// return values of -1 for x and y indicate an error by the caller
+void
+stack_get_slot_position (Stack* self,
+                         Slot   slot,
+                         gint   bubble_height,
+                         gint*  x,
+                         gint*  y)
+{
+	// sanity checks
+	if (!x && !y)
+		return;
+
+	if (!self || !IS_STACK (self))
+	{
+		*x = -1;
+		*y = -1;
+		return;
+	}
+
+	if (slot != SLOT_TOP && slot != SLOT_BOTTOM)
+	{
+		*x = -1;
+		*y = -1;
+		return;
+	}
+
+	// initialize x and y
+	defaults_get_top_corner (self->defaults, x, y);
+
+	// differentiate returned top-left corner for top and bottom slot
+	// depending on the placement 
+	switch (defaults_get_gravity (self->defaults))
+	{
+		Defaults* d;
+
+		case GRAVITY_EAST:
+			d = self->defaults;
+
+			// the position for the sync./feedback bubble
+			if (slot == SLOT_TOP)
+				*y += defaults_get_desktop_height (d) / 2 -
+				      EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
+				      bubble_height +
+				      EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
+			// the position for the async. bubble
+			else if (slot == SLOT_BOTTOM)
+				*y += defaults_get_desktop_height (d) / 2 +
+				      EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
+				      EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
+		break;
+
+		case GRAVITY_NORTH_EAST:
+			d = self->defaults;
+
+			// there's nothing to do for slot == SLOT_TOP as we
+			// already have correct x and y from the call to
+			// defaults_get_top_corner() earlier
+
+			// this needs to look at the height of the bubble in the
+			// top slot
+			if (slot == SLOT_BOTTOM)
+			{
+				g_assert (stack_is_slot_vacant (self, SLOT_TOP) == OCCUPIED);
+				*y += bubble_get_height (self->slots[SLOT_TOP]) +
+				      EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
+				      2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
+
+			}
+		break;
+
+		default:
+			g_warning ("Unhandled placement!\n");
+		break;
+	}
+}
+
+// call this _before_ the fade-in animation of the bubble starts
+gboolean
+stack_allocate_slot (Stack*  self,
+		     Bubble* bubble,
+                     Slot    slot)
+{
+	// sanity checks
+	if (!self || !IS_STACK (self))
+		return FALSE;
+
+	if (!bubble || !IS_BUBBLE (bubble))
+		return FALSE;
+
+	if (slot != SLOT_TOP && slot != SLOT_BOTTOM)
+		return FALSE;
+
+	if (stack_is_slot_vacant (self, slot))
+		self->slots[slot] = BUBBLE (g_object_ref ((gpointer) bubble));
+	else
+		return FALSE;
+
+	return TRUE;
+}
+
+// call this _after_ the fade-out animation of the bubble is finished
+gboolean
+stack_free_slot (Stack*  self,
+		 Bubble* bubble)
+{
+	// sanity checks
+	if (!self || !IS_STACK (self))
+		return FALSE;
+
+	if (!bubble || !IS_BUBBLE (bubble))
+		return FALSE;
+
+	// check top and bottom slots for bubble pointer equality
+	if (bubble == self->slots[SLOT_TOP])
+	{
+		g_object_unref (self->slots[SLOT_TOP]);
+		self->slots[SLOT_TOP] = NULL;
+	}
+	else if (bubble == self->slots[SLOT_BOTTOM])
+	{
+		g_object_unref (self->slots[SLOT_BOTTOM]);
+		self->slots[SLOT_BOTTOM] = NULL;
+	}
+	else
+		return FALSE;
+
+	return TRUE;	
+}

=== modified file 'src/stack.h'
--- src/stack.h	2009-08-31 13:56:28 +0000
+++ src/stack.h	2009-09-08 13:26:40 +0000
@@ -48,9 +48,18 @@
 
 #define MAX_STACK_SIZE 50
 
+#define VACANT   TRUE
+#define OCCUPIED FALSE
+
 typedef struct _Stack      Stack;
 typedef struct _StackClass StackClass;
 
+typedef enum
+{
+	SLOT_TOP = 0,
+	SLOT_BOTTOM
+} Slot;
+
 /* instance structure */
 struct _Stack
 {
@@ -61,6 +70,7 @@
 	Observer* observer;
 	GList*    list;
 	guint     next_id;
+	Bubble*   slots[2]; // NULL: vacant, non-NULL: occupied
 };
 
 /* class structure */
@@ -114,6 +124,26 @@
 			      gchar** out_version,
 			      gchar** out_spec_ver);
 
+gboolean
+stack_is_slot_vacant (Stack* self,
+                      Slot   slot);
+
+void
+stack_get_slot_position (Stack* self,
+                         Slot   slot,
+			 gint   bubble_height,
+                         gint*  x,
+                         gint*  y);
+
+gboolean
+stack_allocate_slot (Stack*  self,
+		     Bubble* bubble,
+                     Slot    slot);
+
+gboolean
+stack_free_slot (Stack*  self,
+		 Bubble* bubble);
+
 G_END_DECLS
 
 #endif /* __STACK_H */

=== modified file 'tests/test-stack.c'
--- tests/test-stack.c	2009-08-04 17:34:48 +0000
+++ tests/test-stack.c	2009-09-08 13:26:40 +0000
@@ -79,6 +79,111 @@
 	g_object_unref (G_OBJECT (stack));
 }
 
+static void
+test_stack_slots ()
+{
+	Stack*    stack = NULL;
+	Defaults* defaults = defaults_new ();
+	Observer* observer = observer_new ();
+	gint      x;
+	gint      y;
+	Bubble*   one;
+	Bubble*   two;
+
+	stack = stack_new (defaults, observer);
+
+	// check if stack_is_slot_vacant() can take "crap" without crashing
+	g_assert_cmpint (stack_is_slot_vacant (NULL, SLOT_TOP), ==, FALSE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, 832), ==, FALSE);
+	g_assert_cmpint (stack_is_slot_vacant (NULL, 4321), ==, FALSE);
+
+	// check if stack_get_slot_position can take "crap" without crashing
+	stack_get_slot_position (NULL, SLOT_TOP, 0, &x, &y);
+	g_assert_cmpint (x, ==, -1);
+	g_assert_cmpint (y, ==, -1);
+	stack_get_slot_position (stack, 4711, 0, &x, &y);
+	g_assert_cmpint (x, ==, -1);
+	g_assert_cmpint (y, ==, -1);
+	stack_get_slot_position (NULL, 42, 0, NULL, NULL);
+
+	// check if stack_allocate_slot() can take "crap" without crashing
+	one = bubble_new (defaults);
+	two = bubble_new (defaults);
+	g_assert_cmpint (stack_allocate_slot (NULL, one, SLOT_TOP), ==, FALSE);
+	g_assert_cmpint (stack_allocate_slot (stack, NULL, SLOT_TOP), ==, FALSE);
+	g_assert_cmpint (stack_allocate_slot (stack, one, 4711), ==, FALSE);
+
+	// check if stack_free_slot() can take "crap" without crashing
+	g_assert_cmpint (stack_free_slot (NULL, two), ==, FALSE);
+	g_assert_cmpint (stack_free_slot (stack, NULL), ==, FALSE);
+
+	// initially both slots should be empty
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_TOP), ==, VACANT);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_BOTTOM), ==, VACANT);
+	g_object_unref (one);
+	g_object_unref (two);
+
+	// fill top slot, verify it's occupied, free it, verify again
+	one = bubble_new (defaults);
+	g_assert_cmpint (stack_allocate_slot (stack, one, SLOT_TOP), ==, TRUE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_TOP), ==, OCCUPIED);
+	g_assert_cmpint (stack_free_slot (stack, one), ==, TRUE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_TOP), ==, VACANT);
+	g_object_unref (one);
+
+	// fill bottom slot, verify it's occupied, free it, verify again
+	two = bubble_new (defaults);
+	g_assert_cmpint (stack_allocate_slot (stack, two, SLOT_BOTTOM), ==, TRUE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_BOTTOM), ==, OCCUPIED);
+	g_assert_cmpint (stack_free_slot (stack, two), ==, TRUE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_BOTTOM), ==, VACANT);
+	g_object_unref (two);
+
+	// try to free vacant slots
+	one = bubble_new (defaults);
+	two = bubble_new (defaults);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_TOP), ==, VACANT);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_BOTTOM), ==, VACANT);
+	g_assert_cmpint (stack_free_slot (stack, one), ==, FALSE);
+	g_assert_cmpint (stack_free_slot (stack, two), ==, FALSE);
+	g_object_unref (one);
+	g_object_unref (two);
+
+	// allocate top slot, verify, try to allocate top slot again
+	one = bubble_new (defaults);
+	two = bubble_new (defaults);
+	g_assert_cmpint (stack_allocate_slot (stack, one, SLOT_TOP), ==, TRUE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_TOP), ==, OCCUPIED);
+	g_assert_cmpint (stack_allocate_slot (stack, two, SLOT_TOP), ==, FALSE);
+	g_assert_cmpint (stack_free_slot (stack, one), ==, TRUE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_TOP), ==, VACANT);
+	g_object_unref (one);
+	g_object_unref (two);
+
+	// allocate bottom slot, verify, try to allocate bottom slot again
+	one = bubble_new (defaults);
+	two = bubble_new (defaults);
+	g_assert_cmpint (stack_allocate_slot (stack, one, SLOT_BOTTOM), ==, TRUE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_BOTTOM), ==, OCCUPIED);
+	g_assert_cmpint (stack_allocate_slot (stack, two, SLOT_BOTTOM), ==, FALSE);
+	g_assert_cmpint (stack_free_slot (stack, one), ==, TRUE);
+	g_assert_cmpint (stack_is_slot_vacant (stack, SLOT_BOTTOM), ==, VACANT);
+	g_object_unref (one);
+	g_object_unref (two);
+
+	// check if we can get reasonable values from stack_get_slot_position()
+	// FIXME: disabled this test for the moment, hopefully it works within
+	// a real environment
+	/*stack_get_slot_position (stack, SLOT_TOP, &x, &y);
+	g_assert_cmpint (x, >, -1);
+	g_assert_cmpint (y, >, -1);
+	stack_get_slot_position (stack, SLOT_BOTTOM, &x, &y);
+	g_assert_cmpint (x, >, -1);
+	g_assert_cmpint (y, >, -1);*/
+
+	g_object_unref (G_OBJECT (stack));
+}
+
 GTestSuite *
 test_stack_create_test_suite (void)
 {
@@ -91,6 +196,7 @@
 	g_test_suite_add(ts, TC(test_stack_new));
 	g_test_suite_add(ts, TC(test_stack_del));
 	g_test_suite_add(ts, TC(test_stack_push));
+	g_test_suite_add(ts, TC(test_stack_slots));
 
 	return ts;
 }