Discussion:
Introduce panel window list and surface minimize protocol
(too old to reply)
Scott Moreau
2013-03-08 04:47:08 UTC
Permalink
This series implements a window list for the panel. It introduces protocol for
minimize/maximize control of shell_surfaces. It also provides a basic
minimize implementation.

Admittedly I am a little confused about the interface version system. I am
wondering if we should have something such as a changlog file kept in the
protocol folder.

Aside from this, the window list seems to work pretty well with all the new
functionality that it offers. Minimized buttons are hooked up by this series
as well, which means we can start fleshing out the decorations in other clients
and toolkits. The list operation is dynamic, supporting multiple outputs.

Changed in v3:

- Rebased to latest master
- Addressed everything listed here:
http://lists.freedesktop.org/archives/wayland-devel/2012-November/006485.html
- Additional bug fixes


Here is a breif video clip demoing the latest code:


Scott Moreau
2013-03-08 04:47:09 UTC
Permalink
In order for clients to notify the compositor that they wish to be minimized, a
minimize request is needed. This can be used to minimize the surface when a
user clicks a minimize button for example.

The compositor needs a way to tell clients to maximize and unmaximize their
surfaces. The desktop shell client can ask the compositor to send a surface
an (un)maximize event, in response to a panel button for example.

The compositor can minimize and unminimize surfaces but clients can only
request that its surface is minimized. The client doesn't need to be involved
in a minimize action, unlike (un)maximize, it only needs a way to track its
minimize state and request to be minimized. This patch adds minimize and
unminimize protocol events for this purpose.

Further, the term minimize is relatively subjective and defined by the
implementation. Clients should not expect that minimized means the surface
will be invisable to the user. There are several use cases where displaying
minimized surfaces will be useful. Clients might want to change input handling
or pause when minimized but nothing should change with regards to submitting
surface buffer updates.
---
protocol/wayland.xml | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/protocol/wayland.xml b/protocol/wayland.xml
index 0ce68ef..8fb5841 100644
--- a/protocol/wayland.xml
+++ b/protocol/wayland.xml
@@ -527,7 +527,7 @@
</request>
</interface>

- <interface name="wl_shell_surface" version="1">
+ <interface name="wl_shell_surface" version="2">

<description summary="desktop style meta data interface">
An interface implemented by a wl_surface. On server side the
@@ -729,6 +729,42 @@
to the client owning the popup surface.
</description>
</event>
+
+ <!-- Version 2 additions -->
+
+ <request name="set_minimized">
+ <description summary="request minimize">
+ A request from the client to notify the compositor that it wants to be
+ minimized.
+ </description>
+ </request>
+
+ <event name="maximize">
+ <description summary="suggest maximize">
+ The maximize event is to signal to the client that it should become
+ maximized.
+ </description>
+ </event>
+
+ <event name="unmaximize">
+ <description summary="suggest unmaximize">
+ The unmaximize event is to signal to the client that it should become
+ unmaximized.
+ </description>
+ </event>
+
+ <event name="minimize">
+ <description summary="minimize notification">
+ The minimize event is to notify the client that it has been minimized.
+ </description>
+ </event>
+
+ <event name="unminimize">
+ <description summary="unminimize notification">
+ The unminimize event is to notify the client that it has been
+ unminimized.
+ </description>
+ </event>
</interface>

<interface name="wl_surface" version="2">
--
1.7.10.4
Bill Spitzak
2013-03-08 19:50:49 UTC
Permalink
Post by Scott Moreau
The client doesn't need to be involved
in a minimize action, unlike (un)maximize, it only needs a way to track its
minimize state and request to be minimized.
No, please allow the client to decide exactly what surfaces to unmap in
response to minimize.

I need the ability to make floating windows that are shared by more than
one surface. A request to minimize one of those surfaces can only be
correctly handled by the client, unless you start sending incredibly
complex DAG information from the clients to the server which I think
should be avoided at all costs. Any workaround is going to make the
floating windows blink which is against Wayland design principles.

In general, the client must ALWAYS have last say over what surfaces are
visible and what their stacking order is. The compositor NEVER NEVER
NEVER changes this except in response to a request from the client.
Scott Moreau
2013-03-08 20:02:59 UTC
Permalink
Post by Scott Moreau
The client doesn't need to be involved
Post by Scott Moreau
in a minimize action, unlike (un)maximize, it only needs a way to track its
minimize state and request to be minimized.
No, please allow the client to decide exactly what surfaces to unmap in
response to minimize.
I need the ability to make floating windows that are shared by more than
one surface. A request to minimize one of those surfaces can only be
correctly handled by the client, unless you start sending incredibly
complex DAG information from the clients to the server which I think should
be avoided at all costs. Any workaround is going to make the floating
windows blink which is against Wayland design principles.
In general, the client must ALWAYS have last say over what surfaces are
visible and what their stacking order is. The compositor NEVER NEVER NEVER
changes this except in response to a request from the client.
I don't know what you're talking about here but please see the comment in
the last paragraph of the commit message:

"Further, the term minimize is relatively subjective and defined by the
implementation. Clients should not expect that minimized means the surface
will be invisable to the user. There are several use cases where displaying
minimized surfaces will be useful."

Minimize can be handled differently by each compositor. The protocol does
not define minimize explicitly. The important part is that the protocol is
in place so that the compositor and clients can communicate minimize state
information, not unlike maximize. The comment you're looking at does not
represent any protocol restriction, it's merely a reminder that suggests a
minimize surface might not be unmapped. We might want to view 'live'
minimized surfaces in a window preview, graphical window switcher or
scaling feature. It seems that you're misinterpreting this specific text
but I'm not really sure what you mean. Just know that the weston
implementation is a reference with working proof-of-concepts, exercising
and demonstrating the protocol. A different wayland compositor can handle
all of these events and requests differently.

- Scott
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/wayland-devel/attachments/20130308/c3f3e193/attachment.html>
Bill Spitzak
2013-03-08 22:24:05 UTC
Permalink
Post by Scott Moreau
I don't know what you're talking about here but please see the comment
The concern is exactly the same as for any subsurfaces and for child
floating windows.

The specific example is a client has any number of "main" surfaces and a
single "toolbox" floating above it. Assume the toolbox should disappear
if and ONLY if all the "main" surfaces are minimized.

I assume you are intending a solution that works for the case of one
"main" surface. This would be done by an additional client api to
indicate that the toolbox is a "child" of the main surface, and the
shell would know to unmap all the "children" when the main surface is
minimized.

Now what to do if there are two "main" surfaces? If the toolbox is a
child of the one you close then it will vanish. If it is not a child of
any then it won't vanish even if you close both, plus it will show up in
the panel.

The only solution is to convey an entire directed acyclic graph of
window descendents from the client to the compositor. This would be
*fiendishly* complex due to the need of the client to be able to make
arbitrary changes in an atomic way combined with the creation,
destruction, and reordering of surfaces. I do not think there is a
workable method other than dumping the entire new DAG as a single
request (as computing a non-blinking minimum set of changes from one DAG
to another is impossible). There are also really annoying problems with
making the changes atomic with the changes of contents of windows so
that at no moment does the user see incorrect window contents different
than the current stacking order.

It is instead trivial for the client to just receive a "minimize" event
and respond by unmapping the main window and the toolbox if it is the
only main window. This is extremely reliable and easy.

I do propose that the client be able to send a "tree" of window
relationships to the compositor (a tree is different than a DAG as there
is at most one path from one node to another). This is simpler for two
reasons: it can be updated reliably with single atomic "change the
parent of x to y" calls, and second because I want the changes to have
no effect on the window stacking or appearance (the clients are expected
to fix this to match as they update the tree). The tree is useful for
the panel and similar third party programs to introspect the setup of
surfaces, that is it's only purpose.
Post by Scott Moreau
"Further, the term minimize is relatively subjective and defined by the
implementation. Clients should not expect that minimized means the surface
will be invisable to the user. There are several use cases where displaying
minimized surfaces will be useful."
This would be supported by having the "minimize request" *not* send a
"minimize event" from the compositor to the client. If the result is
something other than "minimize is ignored" then it would send a
different event describing the desired result.

Really I find it hard to believe a design that requires knowing whether
the desktop or tablet shell is being used would have problems with the
client also knowing what type of minimize the shell is using.
Bill Spitzak
2013-03-08 22:28:58 UTC
Permalink
Post by Scott Moreau
"Further, the term minimize is relatively subjective and defined by the
implementation. Clients should not expect that minimized means the surface
will be invisable to the user. There are several use cases where displaying
minimized surfaces will be useful."
Minimize can be handled differently by each compositor. The protocol
does not define minimize explicitly. The important part is that the
protocol is in place so that the compositor and clients can communicate
minimize state information, not unlike maximize. The comment you're
looking at does not represent any protocol restriction, it's merely a
reminder that suggests a minimize surface might not be unmapped. We
might want to view 'live' minimized surfaces in a window preview,
graphical window switcher or scaling feature. It seems that you're
misinterpreting this specific text but I'm not really sure what you
mean. Just know that the weston implementation is a reference with
working proof-of-concepts, exercising and demonstrating the protocol. A
different wayland compositor can handle all of these events and requests
differently.
Actually perhaps I am misunderstanding it. Does it just send an "unmap
request" from the shell to the client? From the code it seems to cause
the compositor to stop showing the minimized window without any
indication being sent to the client at all, which I absolutely disagree
with!

If in fact the window will not vanish until the client responds to the
unmap request, that will allow the client to atomically unmap child
windows if wanted.

I'm not sure if that is a good idea to have the "unmap request" without
an indication that it is due to a minimize, though. Maybe there are
multiple reasons for an unmap request and clients may want to respond
differently to them.
Scott Moreau
2013-03-08 22:39:29 UTC
Permalink
Post by Scott Moreau
"Further, the term minimize is relatively subjective and defined by the
Post by Scott Moreau
implementation. Clients should not expect that minimized means the surface
will be invisable to the user. There are several use cases where displaying
minimized surfaces will be useful."
Minimize can be handled differently by each compositor. The protocol does
not define minimize explicitly. The important part is that the protocol is
in place so that the compositor and clients can communicate minimize state
information, not unlike maximize. The comment you're looking at does not
represent any protocol restriction, it's merely a reminder that suggests a
minimize surface might not be unmapped. We might want to view 'live'
minimized surfaces in a window preview, graphical window switcher or
scaling feature. It seems that you're misinterpreting this specific text
but I'm not really sure what you mean. Just know that the weston
implementation is a reference with working proof-of-concepts, exercising
and demonstrating the protocol. A different wayland compositor can handle
all of these events and requests differently.
Actually perhaps I am misunderstanding it. Does it just send an "unmap
request" from the shell to the client? From the code it seems to cause the
compositor to stop showing the minimized window without any indication
being sent to the client at all, which I absolutely disagree with!
If in fact the window will not vanish until the client responds to the
unmap request, that will allow the client to atomically unmap child windows
if wanted.
I'm not sure if that is a good idea to have the "unmap request" without an
indication that it is due to a minimize, though. Maybe there are multiple
reasons for an unmap request and clients may want to respond differently to
them.
I am not really sure what you are talking about but I'm also not sure I
have time for it. The fact is that this is only a basic implementation to
exercise the new protocol. If you would like to contribute code, the policy
is that patches are welcome. A working implementation of what you think is
better might also help to illustrate your points better.

- Scott
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/wayland-devel/attachments/20130308/a71196bc/attachment.html>
Jason Ekstrand
2013-03-18 01:24:36 UTC
Permalink
Post by Bill Spitzak
Post by Scott Moreau
"Further, the term minimize is relatively subjective and defined by the
implementation. Clients should not expect that minimized means the surface
will be invisable to the user. There are several use cases where displaying
minimized surfaces will be useful."
Minimize can be handled differently by each compositor. The protocol does
not define minimize explicitly. The important part is that the protocol is
in place so that the compositor and clients can communicate minimize state
information, not unlike maximize. The comment you're looking at does not
represent any protocol restriction, it's merely a reminder that suggests a
minimize surface might not be unmapped. We might want to view 'live'
minimized surfaces in a window preview, graphical window switcher or scaling
feature. It seems that you're misinterpreting this specific text but I'm not
really sure what you mean. Just know that the weston implementation is a
reference with working proof-of-concepts, exercising and demonstrating the
protocol. A different wayland compositor can handle all of these events and
requests differently.
Actually perhaps I am misunderstanding it. Does it just send an "unmap
request" from the shell to the client? From the code it seems to cause the
compositor to stop showing the minimized window without any indication being
sent to the client at all, which I absolutely disagree with!
If in fact the window will not vanish until the client responds to the
unmap request, that will allow the client to atomically unmap child windows
if wanted.
I'm not sure if that is a good idea to have the "unmap request" without an
indication that it is due to a minimize, though. Maybe there are multiple
reasons for an unmap request and clients may want to respond differently to
them.
I am not really sure what you are talking about but I'm also not sure I have
time for it. The fact is that this is only a basic implementation to
exercise the new protocol. If you would like to contribute code, the policy
is that patches are welcome. A working implementation of what you think is
better might also help to illustrate your points better.
That's not really a good answer when we're talking about the core
protocol. If this is so experimental, it should be added as a weston
extension, not to the wayland core. Before we start adding things to
the wayland core protocol, we need to get it right.

Also, I think I have to agree with Bill that this is a bit simplistic.
The clients should have control over how things get minimized. Also,
we probably don't want to simply unmap the surface. As I understand,
unmapping is currently done by removing the buffer from the surface.
Many desktop environments have a window preview in the switcher or
somewhere else. If you want to be able to preview minimized windows,
it needs to remain mapped.

I guess I don't necessarily have a problem with the events/requests
specified, simply that I think thinks need to be clarified and thought
out more. For example, does the compositor send a minimize event to
the client and expect it to minimize windows or does it simply
minimize it? I don't think we want the compositor hiding client
windows without letting the client override it.

I can provide more thoughts later, but there's my initial thoughts.
--Jason Ekstrand
Scott Moreau
2013-03-18 02:02:58 UTC
Permalink
Hi Jason,

Thanks for your reply.
Post by Jason Ekstrand
Post by Bill Spitzak
Post by Scott Moreau
"Further, the term minimize is relatively subjective and defined by the
implementation. Clients should not expect that minimized means the surface
will be invisable to the user. There are several use cases where displaying
minimized surfaces will be useful."
Minimize can be handled differently by each compositor. The protocol does
not define minimize explicitly. The important part is that the protocol is
in place so that the compositor and clients can communicate minimize state
information, not unlike maximize. The comment you're looking at does not
represent any protocol restriction, it's merely a reminder that suggests a
minimize surface might not be unmapped. We might want to view 'live'
minimized surfaces in a window preview, graphical window switcher or scaling
feature. It seems that you're misinterpreting this specific text but I'm not
really sure what you mean. Just know that the weston implementation is a
reference with working proof-of-concepts, exercising and demonstrating the
protocol. A different wayland compositor can handle all of these events and
requests differently.
Actually perhaps I am misunderstanding it. Does it just send an "unmap
request" from the shell to the client? From the code it seems to cause the
compositor to stop showing the minimized window without any indication being
sent to the client at all, which I absolutely disagree with!
If in fact the window will not vanish until the client responds to the
unmap request, that will allow the client to atomically unmap child windows
if wanted.
I'm not sure if that is a good idea to have the "unmap request" without an
indication that it is due to a minimize, though. Maybe there are multiple
reasons for an unmap request and clients may want to respond differently to
them.
I am not really sure what you are talking about but I'm also not sure I have
time for it. The fact is that this is only a basic implementation to
exercise the new protocol. If you would like to contribute code, the policy
is that patches are welcome. A working implementation of what you think is
better might also help to illustrate your points better.
That's not really a good answer when we're talking about the core
protocol. If this is so experimental, it should be added as a weston
extension, not to the wayland core. Before we start adding things to
the wayland core protocol, we need to get it right.
This series provides a complete example implementation in weston.
Maximize and fullscreen are part of wl_shell_interface. It wouldn't
make sense to put it anywhere else, than the same place as these
existing events/requests.
Post by Jason Ekstrand
Also, I think I have to agree with Bill that this is a bit simplistic.
The clients should have control over how things get minimized.
What do you mean by this? Is there something more to minimize than
either minimized/iconified or not?
Post by Jason Ekstrand
Also,
we probably don't want to simply unmap the surface. As I understand,
unmapping is currently done by removing the buffer from the surface.
Many desktop environments have a window preview in the switcher or
somewhere else. If you want to be able to preview minimized windows,
it needs to remain mapped.
Yes, we probably want to make sure that clients wont be starved for
frame events, which is the case currently when pulling the surface
from the render list. It does not do an official 'unmap' though, i.e.
it does not use the unmap call in the weston shell plugin.
Post by Jason Ekstrand
I guess I don't necessarily have a problem with the events/requests
specified, simply that I think thinks need to be clarified and thought
out more. For example, does the compositor send a minimize event to
the client and expect it to minimize windows or does it simply
minimize it? I don't think we want the compositor hiding client
windows without letting the client override it.
Yes, this is another catch. Minimize actually doesn't require client
support, the compositor can just 'unmap' it (using the term loosely
here). It certainly could use more thought. The main thing is, we want
to test real-world use cases and make sure they behave as expected. So
far since rebasing, I've been testing and haven't noticed any real
issues other than lack of frame events sent to minimized clients. The
gh next branches for wayland/weston has this series all applied and
working, in case anyone would like to tinker with it.
Post by Jason Ekstrand
I can provide more thoughts later, but there's my initial thoughts.
Thanks again for your concern.
Post by Jason Ekstrand
--Jason Ekstrand
Now I would like to take this opportunity to relate some new
information I have received from Kristian on IRC. It has come to my
attention that weston is not intended to be used as a real desktop
environment at all. From IRC:

<krh> the desktop in weston is a toy

Also these comments:

<krh> weston isn't going to be a full DE, starting a new DE is
specifically a non-goal of wayland
<krh> and I've always said that fleshing out wl_shell will have to
wait until we have at least one real DE to driver the work
<krh> otherwise it's all just going to be guesswork

This is unfortunate, because several things change. First, despite
Kristian showing interest in this window list series by listing it on
the patch queue emails
http://lists.freedesktop.org/archives/wayland-devel/2013-February/007328.html,
he has unofficially rejected these patches in their entirety due to
the fact that they have DE-like goals in mind. Second, I personally
like weston and I think it has potential to become a DE in it's own
right with a little diligence and some time. Third, Kristian has
expressed no interest in the gh next series or the benefits that it
might provide. So, in light of these new developments, I am going to
continue working on gh next from a DE perspective, attempting to keep
rebased to official upstream repos and pull patches of interest from
the mailing list. gh next has made much progress in the few days since
the announcement. Thanks for everyone's help and as always, patches
welcome. I intend to make a separate post detailing the gh next
progress soon.

@Krisitian:

I am pretty discouraged by the stagnant activity of weston and by your
decisions to lead people on. I would like to request that you do not
post any more of my patches in the patch queue, because this makes me
think that these were definite patches of interest for inclusion to
the official repositories. I don't know why you would have people
believe, that their work is desired when in fact it is not. Further, I
don't think that any work can be considered guesswork when there is a
working implementation spanning several projects. If I am
misunderstanding anything here, please feel free to correct me.

- Scott
Jason Ekstrand
2013-03-18 19:10:48 UTC
Permalink
Scott,
Allow me to be a bit more specific as to what I am thinking. In
general, I like the requests and events in the protocol, I just think
things should be set out more clearly. The documentation given in the
protocol isn't terribly specific as to how someone is supposed to
implement it. From reading your code, it seems like you simply have
this magic "minimized" state that you're trying to keep in sync.
Unfortunately, it's not terribly clear.

Here is how I think I would have such a protocol work. (Perhaps this
is what you intend, but, like I said, it's kind of hard to tell.)

If the minimize event comes from compositor-side:
1. The compositor sends a "request_minimize" event to the client (or
just "minimize", but I like the other a little better)
2. The client responds with "set_minimized" events on any number of
top-level surfaces.
3. The compositor minimizes the top-level surfaces as requested by the client

If the minimize event originates client-side:
1. The client sends calls "set_minimized" on any number of top-level surfaces
2. The compositor minimizes the top-level surfaces as requested by the client

In this way the client remains in control of what windows get
minimized etc. I think it also makes it more explicit as to when a
window is/isn't max/minimized. I also like this addition because it
allows for the possibility of server-side decorations. Not that those
are going to be a for-sure thing, but allowing the compositor to relay
these kinds of events to the client is very useful. Along these
lines, a "request_close" event is probably not a bad idea. That said,
I think things should be client-controlled not compositor-controlled
as it leads to fewer edge-cases.

As far as use-cases go, I think there are a number of things to think about:
1. A window that does not want to be maximized. What if the window
wants to have a fixed size? Do you simply set it as maximized and let
it fail the configure event? In that case the compositor thinks it's
maximized but, in reality, it's not.

2. A window that, when you minimize it actually minimizes other
windows as well (dialogues for example). This is where Bill's example
falls. If you want it more concrete, think about the original
multi-window layout of GIMP. I don't want the dialogues visible
whenever at least one image window is visible and invisible when none
of the image windows are visible. This could probably be handled in a
compositor-dictated system, but it will look strange if the image
window gets minimized and then, because of lag, the dialogue gets
removed a few seconds later.

3. A window that, when you maximize it, responds by maximizing
something else. Consider a video player. It may be using a
subsurface to display its video and then, when you press the maximize
button it actually detaches the video surface and sets it to
fullscreen. Again, in a compositor-dominated environment this would
really confuse the compositor.

Hopefully this is concrete enough to demonstrate my point.
Post by Scott Moreau
Post by Jason Ekstrand
I am not really sure what you are talking about but I'm also not sure I have
time for it. The fact is that this is only a basic implementation to
exercise the new protocol. If you would like to contribute code, the policy
is that patches are welcome. A working implementation of what you think is
better might also help to illustrate your points better.
That's not really a good answer when we're talking about the core
protocol. If this is so experimental, it should be added as a weston
extension, not to the wayland core. Before we start adding things to
the wayland core protocol, we need to get it right.
This series provides a complete example implementation in weston.
Maximize and fullscreen are part of wl_shell_interface. It wouldn't
make sense to put it anywhere else, than the same place as these
existing events/requests.
Yes, it is an implementation. However, I think there are some
corner-cases and things that need to be thought about. Part of my
point above is that you can't blow off the people who review your code
with "I don't have time" and then expect Kristian to apply your
patches. We need to be careful with the core protocol and make sure
we get it right. There is a big limit as to how much of the
"implement it now and fix it later" we can do with the core protocol.
As soon as we have the 1.1 release, we (and the rest of the Linux
community) are stuck with it regardless of whether or not it's a good
implementation.

I hope that's more clear,
--Jason Ekstrand
Scott Moreau
2013-03-18 20:24:17 UTC
Permalink
Hi Jason,

First I'd like to thank you for your attention to this matter. I have
been looking for someone to address this series so I can bounce ideas
around and discuss this. There are many interesting possibilities
here. You hit many points dead-on. Comments below.
Post by Jason Ekstrand
Scott,
Allow me to be a bit more specific as to what I am thinking. In
general, I like the requests and events in the protocol, I just think
things should be set out more clearly.
Yes, I agree.
Post by Jason Ekstrand
The documentation given in the
protocol isn't terribly specific as to how someone is supposed to
implement it.
Yes, I have been waiting for someone to review this series so that we
can openly discuss it and finally, agree on what events, requests
should be used but possibly more importantly, their descriptions that
will be the protocol documentation.
Post by Jason Ekstrand
From reading your code, it seems like you simply have
this magic "minimized" state that you're trying to keep in sync.
Unfortunately, it's not terribly clear.
Yes, it is not clear I agree. It is a working proof-of-concept but the
way it should be implemented is not outlined in the protocol. I fought
a bit with this at first, trying to come up with descriptions,
especially for minimize. Instead of wasting time on trying to come up
with anything, I just put the protocol in place with generic
descriptions. I know that this is an important topic and the
descriptions should be as correct as possible, so compositors and
clients know exactly what to do.
Post by Jason Ekstrand
Here is how I think I would have such a protocol work. (Perhaps this
is what you intend, but, like I said, it's kind of hard to tell.)
1. The compositor sends a "request_minimize" event to the client (or
just "minimize", but I like the other a little better)
2. The client responds with "set_minimized" events on any number of
top-level surfaces.
3. The compositor minimizes the top-level surfaces as requested by the client
1. The client sends calls "set_minimized" on any number of top-level surfaces
2. The compositor minimizes the top-level surfaces as requested by the client
Yes, I completely agree. This is the same semantics of the current
maximize implementation.
Post by Jason Ekstrand
In this way the client remains in control of what windows get
minimized etc. I think it also makes it more explicit as to when a
window is/isn't max/minimized. I also like this addition because it
allows for the possibility of server-side decorations. Not that those
are going to be a for-sure thing, but allowing the compositor to relay
these kinds of events to the client is very useful.
Yep.
Post by Jason Ekstrand
Along these
lines, a "request_close" event is probably not a bad idea.
Giulio Camuffo had the exact same idea. He wrote some patches and it
is now in gh next wayland/weston/gtk/qtwayland repos. This allows us
to close individual surfaces from a common client and not just blindly
destroy the entire client. This allows us to have the close function
in the window list to work per-surface and not per-client as it was.
Post by Jason Ekstrand
That said,
I think things should be client-controlled not compositor-controlled
as it leads to fewer edge-cases.
Yes, this is the way it works for maximize and there is no reason why
it shouldn't work the same for minimize.
Post by Jason Ekstrand
1. A window that does not want to be maximized. What if the window
wants to have a fixed size? Do you simply set it as maximized and let
it fail the configure event? In that case the compositor thinks it's
maximized but, in reality, it's not.
The client is in control, as you detailed how it should work with
minimize. So you ask the client to maximize and if it doesn't send the
maximize request, it never gets maximized. We can't possibly
force-maximize a surface without client support lest we get garbage
buffers. So, this is how it already works and a non-issue.
Post by Jason Ekstrand
2. A window that, when you minimize it actually minimizes other
windows as well (dialogues for example). This is where Bill's example
falls. If you want it more concrete, think about the original
multi-window layout of GIMP. I don't want the dialogues visible
whenever at least one image window is visible and invisible when none
of the image windows are visible. This could probably be handled in a
compositor-dictated system, but it will look strange if the image
window gets minimized and then, because of lag, the dialogue gets
removed a few seconds later.
You kinda lost me here but the client can already send a minimize
request per surface. Now that you mention it, we probably should have
an unminimize request as well. This way we can do what (if I
understand correctly) you're talking about here. This would also let
clients unminimize select surfaces in response to a 'tray icon' right
click menu function for example.
Post by Jason Ekstrand
3. A window that, when you maximize it, responds by maximizing
something else. Consider a video player. It may be using a
subsurface to display its video and then, when you press the maximize
button it actually detaches the video surface and sets it to
fullscreen. Again, in a compositor-dominated environment this would
really confuse the compositor.
I am thinking this is another non-issue. Clients are in control of
sending fullscreen or maximize events for their surfaces and setting
them back (toplevel).
Post by Jason Ekstrand
Hopefully this is concrete enough to demonstrate my point.
Yes and again, I would like to thank you for taking the time out to
address this. I now have a couple of other outstanding cases I would
like to introduce to the discussion. These are cases to consider when
thinking about what descriptions should be used for the minimize
protocol documentation.

1) We want live minimized surface previews. This means that we want to
be able to display a minimized surface in several cases and have the
client still rendering frames of whatever it draws. For example, a
hover action on the panel's window list item could pop-up a scaled
down preview of the surface. Another use case is for 'scale' (compiz)
or 'expose' (osx) effect. We want to be able to view minimized surface
in this mode as well. There are plenty of other use cases as well. The
question is, how should the semantics work here? Should the client
become unminimized for this case? Or should the client do nothing but
just always keep rendering and attaching frames even in the minimized
case?

2) Minimized surfaces do not receive frame events. The current
implementation removes the surface from the compositor render list of
surfaces to draw. This means that a) the surface has no assigned
output and b) does not get sent frame events while minimized. I
thought about this and ended up with a separate minimized surface list
that contains the list of currently minimized surfaces. Should output
repaint send frame events to minimized surfaces as well?

3) Currently, most all wayland clients depend on frame events to drive
their rendering. If the clients are not sent these frame events, the
client rendering will stall. This causes problems for video players
reliant on the frame event. Should we expect all clients to render not
depending on the frame events? It is not clear from the frame event
protocol description. if this is the case, we should add it to the
protocol description and change the clients accordingly. As I
understand, eglSwapBuffers() automatically blocks on each call to sync
properly. So even if you called it at the 'wrong' time (i.e. outside
of the frame event handler) it would still sync in the egl case. I'm
not sure about how shm would work here.


Thanks,

Scott
Post by Jason Ekstrand
Post by Jason Ekstrand
Post by Jason Ekstrand
I am not really sure what you are talking about but I'm also not sure I have
time for it. The fact is that this is only a basic implementation to
exercise the new protocol. If you would like to contribute code, the policy
is that patches are welcome. A working implementation of what you think is
better might also help to illustrate your points better.
That's not really a good answer when we're talking about the core
protocol.
Yes you are right. Unfortunately, I typically disregard Bill's
opinions because they are not well organized, often over-thought and
he never submits any code at all. I don't really have time for it.
Post by Jason Ekstrand
Post by Jason Ekstrand
If this is so experimental, it should be added as a weston
Post by Jason Ekstrand
extension, not to the wayland core. Before we start adding things to
the wayland core protocol, we need to get it right.
This series provides a complete example implementation in weston.
Maximize and fullscreen are part of wl_shell_interface. It wouldn't
make sense to put it anywhere else, than the same place as these
existing events/requests.
Yes, it is an implementation. However, I think there are some
corner-cases and things that need to be thought about. Part of my
point above is that you can't blow off the people who review your code
with "I don't have time" and then expect Kristian to apply your
patches.
I never expect my patches are pushed upstream to the official repos
without proper review. It is the review I'm looking for, so that
problems can be addressed and fixed, for eventual potential inclusion
and use for all. Unfortunately, since Kristian has finally announced
that he is not interested in any patches with DE-like goals from
anyone other than DE developers. The DE part here is the window list
and surface_data protocol. However, the surface_data protocol is just
local weston protocol. The minimize/maximize items are things that
should be in core protocol regardless if they provide DE-like
functionality or not.
Post by Jason Ekstrand
We need to be careful with the core protocol and make sure
we get it right.
Yes, I fully agree. And *again* I want to thank for your time and
attention to this. I have been *looking* for a reviewer, though I was
expecting it to be krh. I think your ideas are right on target.
Post by Jason Ekstrand
There is a big limit as to how much of the
"implement it now and fix it later" we can do with the core protocol.
Yes, you are right. This is why I want to have a working
implementation in weston, as it should serve as the official reference
implementation for all.
Post by Jason Ekstrand
As soon as we have the 1.1 release, we (and the rest of the Linux
community) are stuck with it regardless of whether or not it's a good
implementation.
Indubitably.
Post by Jason Ekstrand
I hope that's more clear,
It does, all I wanted was some attention from someone with experience.

Note to Bill Spitzac: I find your posts to be often frivolous and
incoherent. I don't mean to be rude here but I have tried to consider
many of your points and you often go on long tangents about some
problem that doesn't exist in reality or a highly isolated use case.
Many times I see your postings as a frivolous spec in a ml paragraph
of how you think something should work. My point here is, if you want
something to work a certain way, then write the code. This way, people
can review and comment on your code (not your random comments) or you
can just use it for yourself. I would like to ask that if you do
respond, please put in the time and effort to make your thoughts
coherent and in the scope of the wayland reference compositor.
Post by Jason Ekstrand
--Jason Ekstrand
- Scott Moreau
Bill Spitzak
2013-03-18 21:29:12 UTC
Permalink
Post by Scott Moreau
Note to Bill Spitzac: I find your posts to be often frivolous and
incoherent. I don't mean to be rude here but I have tried to consider
many of your points and you often go on long tangents about some
problem that doesn't exist in reality or a highly isolated use case.
Many times I see your postings as a frivolous spec in a ml paragraph
of how you think something should work. My point here is, if you want
something to work a certain way, then write the code. This way, people
can review and comment on your code (not your random comments) or you
can just use it for yourself. I would like to ask that if you do
respond, please put in the time and effort to make your thoughts
coherent and in the scope of the wayland reference compositor.
I have gotten zero feedback (not even somebody saying "this is
incorrect") for any patches I have submitted so far. I have to say this
is a little discouraging.

I agree my posts tend to specify an implementation rather than the
desired results, though it would seem that source code is going even
further in that direction.

I can try some pictures to show what I think the minimize must be able
to support:

#1:
+--------+
| |
| MAIN1 |---------+
| | |
| +--------+ |
| | DIALOG | |
| +--------+ |
+--------+ MAIN 2 |
+----------+

After "minimize" of MAIN1, this is the desired result:

#2:
+----------+
| |
+--------+ |
| DIALOG | |
+--------+ |
| MAIN 2 |
+----------+

After "minimize" of MAIN 2 this is the desired result:

#3:
<blank>

The API must be designed so that no composite other than the initial and
final is ever produced, even for a split second, for each of these
transitions. By "other composite" I mean any different stacking order or
any where the set of visibility of surfaces is different.

For instance if minimize hid the dialog but the client could put it
back, this would make the transition from #1 to #2 have an intermediate
composite where MAIN 2 was visible but not the dialog.

If the compositor did not hide the dialog but the client did it in
response to the minimize, then the transition from #2 to #3 would result
in an intermediate display where only the dialog is visible.

I believe the only solution is for the compostior to send
minimize-request to the client, and the client then has to minimize the
window (and the dialog if it wants) and then to a commit to make it
atomic. However I agree that trying to specify a solution would be
better with source code.
Scott Moreau
2013-03-18 22:06:11 UTC
Permalink
Post by Scott Moreau
Note to Bill Spitzac: I find your posts to be often frivolous and
incoherent. I don't mean to be rude here but I have tried to consider
many of your points and you often go on long tangents about some
problem that doesn't exist in reality or a highly isolated use case.
Many times I see your postings as a frivolous spec in a ml paragraph
of how you think something should work. My point here is, if you want
something to work a certain way, then write the code. This way, people
can review and comment on your code (not your random comments) or you
can just use it for yourself. I would like to ask that if you do
respond, please put in the time and effort to make your thoughts
coherent and in the scope of the wayland reference compositor.
I have gotten zero feedback (not even somebody saying "this is incorrect")
for any patches I have submitted so far. I have to say this is a little
discouraging.
Yes I have found the lack of feedback for patches on the list very
discouraging. It is discouraging to all community members who would
like to contribute.
I agree my posts tend to specify an implementation rather than the desired
results, though it would seem that source code is going even further in that
direction.
Right. The point is, that if you have something laid out in your head
how exactly it should work, then rather than trying to convey these
explicit details to another human over a mailing list and expect them
to read your view, and then accept it as correct and start coding it
for you, the way you dictate, is completely unrealistic. If you
already know precisely how it should work, then you are the one that
should write the code.
I can try some pictures to show what I think the minimize must be able to
+--------+
| |
| MAIN1 |---------+
| | |
| +--------+ |
| | DIALOG | |
| +--------+ |
+--------+ MAIN 2 |
+----------+
+----------+
| |
+--------+ |
| DIALOG | |
+--------+ |
| MAIN 2 |
+----------+
<blank>
The API must be designed so that no composite other than the initial and
final is ever produced, even for a split second, for each of these
transitions. By "other composite" I mean any different stacking order or any
where the set of visibility of surfaces is different.
For instance if minimize hid the dialog but the client could put it back,
this would make the transition from #1 to #2 have an intermediate composite
where MAIN 2 was visible but not the dialog.
If the compositor did not hide the dialog but the client did it in response
to the minimize, then the transition from #2 to #3 would result in an
intermediate display where only the dialog is visible.
I believe the only solution is for the compostior to send minimize-request
to the client, and the client then has to minimize the window (and the
dialog if it wants) and then to a commit to make it atomic. However I agree
that trying to specify a solution would be better with source code.
Patches welcome.

- Scott
Jason Ekstrand
2013-03-21 02:02:56 UTC
Permalink
Bill,
Post by Bill Spitzak
The API must be designed so that no composite other than the initial and
final is ever produced, even for a split second, for each of these
transitions. By "other composite" I mean any different stacking order or any
where the set of visibility of surfaces is different.
I think this is a valid issue, but I'm not sure if we can really solve
it easily by building it into the max/min protocol.

You've brought this issue of synchronization up before on other
topics, so I think it's probably more general than simply maximizing
and minimizing. One thought would be to add some sort of "atomic"
support to the protocol. For instance, you could have "begin_atomic"
and "end_atomic" requests in wl_display that would tell the compositor
that whatever requests it receives between begin_atomic and end_atomic
should occur in a single frame. This would probably require some
additional support in libwayland to handle the threading issues
properly but I think it could probably be done. Obviously such a
thing would have to be used with extreme care and only short sequences
should be atomic.

To be honest, I haven't given that much thought. However if you
wanted to play with it and send a patch to the ML, I'd be more than
happy to take a look at it.

Thanks,
--Jason Ekstrand
Bill Spitzak
2013-03-21 22:39:29 UTC
Permalink
I think (though certainly have not proven) that the current "commit"
mechanism will work for this. The client connects surfaces together into
a tree, and when a change is made to a surface the compositor does not
display the new version until a "commit" is done on either it or a
parent surface. In effect the "begin_atomic" is automatically done for
you, and "commit" is the "end_atomic".

There are currently attempts to use it to synchronize subsurfaces. IMHO
there is absolutely no difference between subsurfaces and floating
windows so I think any solution here will apply to the floating windows.

No matter what scheme is used, however, it does require that the client
decide whether to minimize a surface or not, so it can combine that
minimization with other changes into an atomic operation.
Post by Jason Ekstrand
Bill,
Post by Bill Spitzak
The API must be designed so that no composite other than the initial and
final is ever produced, even for a split second, for each of these
transitions. By "other composite" I mean any different stacking order or any
where the set of visibility of surfaces is different.
I think this is a valid issue, but I'm not sure if we can really solve
it easily by building it into the max/min protocol.
You've brought this issue of synchronization up before on other
topics, so I think it's probably more general than simply maximizing
and minimizing. One thought would be to add some sort of "atomic"
support to the protocol. For instance, you could have "begin_atomic"
and "end_atomic" requests in wl_display that would tell the compositor
that whatever requests it receives between begin_atomic and end_atomic
should occur in a single frame. This would probably require some
additional support in libwayland to handle the threading issues
properly but I think it could probably be done. Obviously such a
thing would have to be used with extreme care and only short sequences
should be atomic.
To be honest, I haven't given that much thought. However if you
wanted to play with it and send a patch to the ML, I'd be more than
happy to take a look at it.
Thanks,
--Jason Ekstrand
Pekka Paalanen
2013-03-19 08:49:06 UTC
Permalink
Hi,

just a fly-by comment from the sickbed here (well, recovering already).

On Mon, 18 Mar 2013 14:24:17 -0600
Post by Scott Moreau
Yes and again, I would like to thank you for taking the time out to
address this. I now have a couple of other outstanding cases I would
like to introduce to the discussion. These are cases to consider when
thinking about what descriptions should be used for the minimize
protocol documentation.
1) We want live minimized surface previews. This means that we want to
be able to display a minimized surface in several cases and have the
client still rendering frames of whatever it draws. For example, a
hover action on the panel's window list item could pop-up a scaled
down preview of the surface. Another use case is for 'scale' (compiz)
or 'expose' (osx) effect. We want to be able to view minimized surface
in this mode as well. There are plenty of other use cases as well. The
question is, how should the semantics work here? Should the client
become unminimized for this case? Or should the client do nothing but
just always keep rendering and attaching frames even in the minimized
case?
2) Minimized surfaces do not receive frame events. The current
implementation removes the surface from the compositor render list of
surfaces to draw. This means that a) the surface has no assigned
output and b) does not get sent frame events while minimized. I
thought about this and ended up with a separate minimized surface list
that contains the list of currently minimized surfaces. Should output
repaint send frame events to minimized surfaces as well?
I don't think you should tie minimized state to the frame event
processing at all. Currently that is just a by-product of Weston
implementation details. To me, these two are completely orthogonal
concepts.

You will have protocol to set a surface minimized or normal. Minimized
probably means, that the surface is not shown as it normally is. It
could be scaled down to a stamp somewhere, not shown at all, or
whatever. I didn't check how you defined what minized means, or if you
even did.

Frame events are tied to the surface being visible. If a compositor
decides to show a surface, in minimized state or not, show it in a
normal way, scaled-down way, or in any way it actually is visible, it
should emit frame events accordingly.

The question "should the client become unminized?" in 1) becomes moot
with this definition, since minimized state would not exclude
visibility.

My answer to the question of 2) is "yes, as usual". If a surface is
visible, it is being repainted, hence frame events should fire.

By "visible" I really mean on the pixel level: if no pixels of a surface
have a chance to affect the compositor output, the surface is not
visible. It's not simply a question of whether a weston_surface is on
some list or has output assigned, occlusions and transformations count
too. I don't recall how far this is implemented in Weston, though, but
this is how I understand the idea of frame events.

As such, clients should not make too much assumptions on when frame
events will occur or not. This is especially important for soft
real-time applications I discuss below.
Post by Scott Moreau
3) Currently, most all wayland clients depend on frame events to drive
their rendering. If the clients are not sent these frame events, the
client rendering will stall. This causes problems for video players
reliant on the frame event. Should we expect all clients to render not
depending on the frame events? It is not clear from the frame event
protocol description. if this is the case, we should add it to the
protocol description and change the clients accordingly. As I
understand, eglSwapBuffers() automatically blocks on each call to sync
properly. So even if you called it at the 'wrong' time (i.e. outside
of the frame event handler) it would still sync in the egl case. I'm
not sure about how shm would work here.
I believe video players should be like any real-time presentation
application, including games, when they use the standard wl_surface
attach/damage/commit sequence to update the window. You send new frames
to the server only when new frame events come, unless you want to
minimize latency by triple-buffering in the client. While waiting for
frame events, you continue the real-time processing regardless: games
run their physics and controls etc., video players continue decoding
the video at the required framerate and produce audio. The
sychronization of e.g. video to audio must be adjusted continuously
like always, and if the application wants, it can try to predict the
video latency from the frame event timestamps.

Whether a stalling frame callback should pause the real-time processing
is application specific. Usually the event that pauses the real-time
processing is something else, like a key-press or minimizing a video
player. But that is up to the application, it might want to continue
playback regardless, the audio is probably heard anyway. Decoded video
frames are just thrown away instead of sent to the server, since it's
hard to skip decoding. Games would preferrably skip calling the
rendering routines.

Btw. eglSwapBuffers behaviour will become adjustable, if it is not
already, by implementing eglSwapInterval in the EGL Wayland platform.
That will allow also EGL apps to render continuously, regardless of
frame events. Or rather, allow EGL apps to explicitly sync to frame
events themselves when they want to.


Thanks,
pq
Jason Ekstrand
2013-03-21 01:56:06 UTC
Permalink
Scott et. al,
I'm not going to try and answer everything because a lot has happened
on this topic and I think we're on the same page on most of the
technical details.
Post by Scott Moreau
Post by Jason Ekstrand
Here is how I think I would have such a protocol work. (Perhaps this
is what you intend, but, like I said, it's kind of hard to tell.)
1. The compositor sends a "request_minimize" event to the client (or
just "minimize", but I like the other a little better)
2. The client responds with "set_minimized" events on any number of
top-level surfaces.
3. The compositor minimizes the top-level surfaces as requested by the client
1. The client sends calls "set_minimized" on any number of top-level surfaces
2. The compositor minimizes the top-level surfaces as requested by the client>
Yes, I completely agree. This is the same semantics of the current
maximize implementation.
Ok, I think we're on the same page: The compositor gives the request
to the client -> client tells the compositor what windows need
changing -> compositor performs changes.

As far as frame callbacks go, like pq said, that's pretty much
orthogonal. I think we should just let the compositor implementation
take care of that. The point here is that none of this should get
implemented by the client removing the buffer from the surface in
order to unmap it. That's unacceptable. Instead, it should happen in
terms of modes.

There is one more question that I think needs to be answered. And
that is: do we handle things in terms of set/unset or in terms of
set_maximized, set_fullscreen, set_minimized, and set_normal (probably
want a better name for that one). Really, which of those two we do
doesn't matter that much because the toolkit can force it either way.
It's mostly a matter of who tracks the state and handles it. I think
I like simply setting the state instead of keeping track of set/unset
better, but I'm open to discussion on that.

Assuming we can get the above question answered, I think I'd like to
see a version 2 with the following changes:

1. Clear and precise documentation. If we're going to require a
client response to a minimize event, it needs to be well-documented.
2. Events for each of: request_maximize, request_minimize, and
request_close (The client should be able to handle fullscreen itself).
I do prefer the request_x because it makes it more clear that the
compositor is requesting the client to minimize, not simply telling it
that it already is.
3. Some way for the client to unset maximized, minimized etc.
4. An enum and a request telling the compositor which of these
operations is supported. This way the compositor can hide the buttons
or whatever. The client can always ignore the request_x events, but
this provides a nicer UI.

Thanks,
--Jason Ekstrand
Scott Moreau
2013-03-21 04:06:22 UTC
Permalink
Hi Jason,
Post by Jason Ekstrand
Scott et. al,
I'm not going to try and answer everything because a lot has happened
on this topic and I think we're on the same page on most of the
technical details.
Post by Scott Moreau
Post by Jason Ekstrand
Here is how I think I would have such a protocol work. (Perhaps this
is what you intend, but, like I said, it's kind of hard to tell.)
1. The compositor sends a "request_minimize" event to the client (or
just "minimize", but I like the other a little better)
2. The client responds with "set_minimized" events on any number of
top-level surfaces.
3. The compositor minimizes the top-level surfaces as requested by the client
1. The client sends calls "set_minimized" on any number of top-level surfaces
2. The compositor minimizes the top-level surfaces as requested by the client>
Yes, I completely agree. This is the same semantics of the current
maximize implementation.
Ok, I think we're on the same page: The compositor gives the request
to the client -> client tells the compositor what windows need
changing -> compositor performs changes.
Yes that's 1). Or 2) the client acts on its own and requests a state
change (client->compositor) 3) A surface_data request is received to
ask the shell to perform a state change on a particular surface_data
object (shell_client->compositor->client)

One important thing to note here is that client != surface. In fact,
clients can have multiple surfaces. We might need to keep this in mind
for things like closing single surfaces demonstrated here
https://github.com/soreau/wayland/commit/65f8a3f5f683c3a91913a26496cc373633f01896

This allows us to tell a client that 'the user has indicated that they
would like one of your surfaces to be closed (this one)'. By way of
contrast, the current code kills the entire client and all of its
surfaces. Unless I am not understanding correctly, we don't have a way
to tell a client to kill only one (or more) of its surfaces with the
current protocol. It might be a good idea to write a test client that
simply has two surfaces from the same client to exercise these cases
(unless there isn't one already). We've been testing with
google-chrome mostly.
Post by Jason Ekstrand
As far as frame callbacks go, like pq said, that's pretty much
orthogonal. I think we should just let the compositor implementation
take care of that.
Yes I meant to comment on this, acknowledging that I understand and
agree with Paalanen's point. Hope you get well soon pq.
Post by Jason Ekstrand
The point here is that none of this should get
implemented by the client removing the buffer from the surface in
order to unmap it. That's unacceptable. Instead, it should happen in
terms of modes.
I don't know what you mean by this really. Two questions at least:
1) What do you mean by modes exactly?
2) What would you do instead of placing it from the drawn list to a
dedicated minimized surface list?
Post by Jason Ekstrand
There is one more question that I think needs to be answered. And
that is: do we handle things in terms of set/unset or in terms of
set_maximized, set_fullscreen, set_minimized, and set_normal (probably
want a better name for that one). Really, which of those two we do
doesn't matter that much because the toolkit can force it either way.
It's mostly a matter of who tracks the state and handles it. I think
I like simply setting the state instead of keeping track of set/unset
better, but I'm open to discussion on that.
I have this all working in gh next. The only thing left to consider
that I can think of is: Do we want to support 'unmaximizing or
unminimizing (or unfullscreening) a surface retains stacking state'.
So basically if there is a bottom-level surface and you state change
it then toggle it back, do we always want it on top no matter what? Or
do we want to optionally support retaining stacking order on state
restore (setting back to 'normal'). If we want to support this
feature, then a new un* request is required for each state set
request. I move that we do support this feature and I'm working on
this in gh next. One other question, do we want to support fullscreen
from a source other than the client? For instance we could have
fullscreen as a selection in the drop down menu? I guess maximize and
minimize are expected features, fullscreen is optional. For this, we'd
need (un)fullscreen events. Hm, I wonder if there's a way to have the
client tell us what states it actually supports so we can correctly
represent this in the panel taskbar menus..

A note on 'request ($state_type) and request (un$state_type)' vs
'request ($state_type, 1) and request ($state_type, 0)':

After looking at the code in a working state, it's far clearer to have
explicit $state and un$state events/request because there are a lot of
paths in the code we have to run through to make this all syn up and
work out properly. Using a state variable to simplify the protocol
will likely complicate the description in the protocol and complicate
the code.
Post by Jason Ekstrand
From your post ago, I wanted to make another related comment so I will
In this way the client remains in control of what windows get
minimized etc. I think it also makes it more explicit as to when a
window is/isn't max/minimized. I also like this addition because it
allows for the possibility of server-side decorations. Not that those
are going to be a for-sure thing, but allowing the compositor to relay
these kinds of events to the client is very useful.
An interesting case is that of xwm toolkit(?) where the decorations
are actually CSD from the eyes of the compositor but compositor side
from xclients POV. So for cases like chrome, you can toggle CSD there
and yea. That's coming up erm, next.
Post by Jason Ekstrand
That said,
I think things should be client-controlled not compositor-controlled
as it leads to fewer edge-cases.
Yes, this is the way it works for maximize and there is no reason why
it shouldn't work the same for minimize. So to recap:

1) Client is ultimately in control of its state. A state request is
assumed 'on'. Restoring will call either toplevel if that is always
the assumption, or the un$state request counterpart to support
stacking order preservation.
2) Compositor and client are responsible for their own state tracking
of each surface
3) One case to keep in mind is clients that have multiple surfaces

Now I'd like to give a stripped down version of what the full featured
proposed protocol would look like:

# - Already in the current wl_shell_surface protocol (also in gh next,
which rebases to master)
n - In gh next
+ - Required to support stack-preservation-upon-state-restoration
= - Required to support fullscreen-function-from-taskbar-menu

n request - minimize
+ request - unminimize
# request - maximize
+ request - unmaximize
# request - fullscreen
+ request - unfullscreen

n event - minimize
n event - unminimize
n event - maximize
n event - unmaximize
= event - fullscreen
= event - unfullscreen

More questions about protocol versioning:

1) If we always add protocol to the end, it will likely be incoherent,
unmatched and not very easy to read. I know wl_shell interface is
disposable but for the sake of clarity how wayland protocol versioning
system, I'd like to know what the expected convention is.
2) I noticed that changing the version requires no changes client
side. How is this supposed to work?
3) The protocol semantics were recently changed. When semantics are
changed of existing protocol does this not constitute an interface
version bump?
Post by Jason Ekstrand
Assuming we can get the above question answered, I think I'd like to
1. Clear and precise documentation. If we're going to require a
client response to a minimize event, it needs to be well-documented.
2. Events for each of: request_maximize, request_minimize, and
request_close (The client should be able to handle fullscreen itself).
I do prefer the request_x because it makes it more clear that the
compositor is requesting the client to minimize, not simply telling it
that it already is.
3. Some way for the client to unset maximized, minimized etc.
4. An enum and a request telling the compositor which of these
operations is supported. This way the compositor can hide the buttons
or whatever. The client can always ignore the request_x events, but
this provides a nicer UI.
Thanks,
--Jason Ekstrand
Jason Ekstrand
2013-03-21 15:39:20 UTC
Permalink
Hi Scott,
Post by Scott Moreau
One important thing to note here is that client != surface. In fact,
clients can have multiple surfaces. We might need to keep this in mind
for things like closing single surfaces demonstrated here
https://github.com/soreau/wayland/commit/65f8a3f5f683c3a91913a26496cc373633f01896
Yes, particularly for the closing case.
Post by Scott Moreau
This allows us to tell a client that 'the user has indicated that they
would like one of your surfaces to be closed (this one)'. By way of
contrast, the current code kills the entire client and all of its
surfaces. Unless I am not understanding correctly, we don't have a way
to tell a client to kill only one (or more) of its surfaces with the
current protocol. It might be a good idea to write a test client that
simply has two surfaces from the same client to exercise these cases
(unless there isn't one already). We've been testing with
google-chrome mostly.
This should be taken care of by the adding a "request_close" surface
event (see previous email).
Post by Scott Moreau
Post by Jason Ekstrand
The point here is that none of this should get
implemented by the client removing the buffer from the surface in
order to unmap it. That's unacceptable. Instead, it should happen in
terms of modes.
1) What do you mean by modes exactly?
2) What would you do instead of placing it from the drawn list to a
dedicated minimized surface list?
This comment merely meant that the client shouldn't minimize a window
by attaching a null buffer. Instead, it should still be a
fully-functioning window (possibly receiving frame events) even though
it's minimized.
Post by Scott Moreau
Post by Jason Ekstrand
There is one more question that I think needs to be answered. And
that is: do we handle things in terms of set/unset or in terms of
set_maximized, set_fullscreen, set_minimized, and set_normal (probably
want a better name for that one). Really, which of those two we do
doesn't matter that much because the toolkit can force it either way.
It's mostly a matter of who tracks the state and handles it. I think
I like simply setting the state instead of keeping track of set/unset
better, but I'm open to discussion on that.
I have this all working in gh next. The only thing left to consider
that I can think of is: Do we want to support 'unmaximizing or
unminimizing (or unfullscreening) a surface retains stacking state'.
So basically if there is a bottom-level surface and you state change
it then toggle it back, do we always want it on top no matter what? Or
do we want to optionally support retaining stacking order on state
restore (setting back to 'normal'). If we want to support this
feature, then a new un* request is required for each state set
request. I move that we do support this feature and I'm working on
this in gh next.
I think stacking should be an orthogonal interface. If we want client
control for it, we need an interface. Otherwise, it should be left as
an implementation detail. I don't think we want to dirty the min/max
protocol with stacking details.
Post by Scott Moreau
One other question, do we want to support fullscreen
from a source other than the client? For instance we could have
fullscreen as a selection in the drop down menu? I guess maximize and
minimize are expected features, fullscreen is optional. For this, we'd
need (un)fullscreen events. Hm, I wonder if there's a way to have the
client tell us what states it actually supports so we can correctly
represent this in the panel taskbar menus..
There's no precedent for the compositor full-screening things. Also,
unless the client is specifically designed to full-screen, you won't
be able to get out of it. For these two reasons, I think we should
leave that entirely up to the client.
Post by Scott Moreau
After looking at the code in a working state, it's far clearer to have
explicit $state and un$state events/request because there are a lot of
paths in the code we have to run through to make this all syn up and
work out properly. Using a state variable to simplify the protocol
will likely complicate the description in the protocol and complicate
the code.
Ok, I think you completely misunderstood what I was trying to say
here. My point is that min/max is a state machine. A window, from
the user's perspective, is minimized, maximized, fullscreen, or
normal. It is never two of those at the same time. My question was
about whether, from a protocol perspective, it should be handled in
terms of setting/unsetting one flag per potential state or whether the
client should simply tell the compositor what state to go to next.
For example, if the window is maximized and you click the unmaximize
button, the client will send the set_normal request instead of
unset_maximized.

This approach has a number of benefits. First, it simplifies the
protocol: fewer requests. Second, it removes most of the state from
the compositor and lets the toolkits deal with it. This is especially
useful when the toolkit may be messing with other windows than the
current one; in that case, the toolkit would have to keep track of all
that state itself anyway. Also, it removes any ambiguity as to what
is going on inside the compositor and keeps the client in control; the
client always knows the window's state.

As a note on set_toplevel. We need to do some thinking here. It came
up on IRC some time back that we may want to have toplevel be a role
(like subsurface) rather than a flag. Now, when we're adding this
max/min jazz might be the time to do that. In that case, I think a
toplevel would be one role and a toplevel surface would have
maximized, minimized, and normal states while full-screen would be its
own role.

If we're re-arranging things like this we could even do it all with a
single set_state request and an enum. (Just a thought, not sure if I
like it.)
Post by Scott Moreau
1) If we always add protocol to the end, it will likely be incoherent,
unmatched and not very easy to read. I know wl_shell interface is
disposable but for the sake of clarity how wayland protocol versioning
system, I'd like to know what the expected convention is.
2) I noticed that changing the version requires no changes client
side. How is this supposed to work?
3) The protocol semantics were recently changed. When semantics are
changed of existing protocol does this not constitute an interface
version bump?
1) They always have to go at the end. Otherwise, older clients will
get confused as to which event you are sending and which request they
are sending.
2) I'm not sure.
3) I'm not sure on some of these things. However, we should only have
to bump the interface version once we've released the new interface.
Scott Moreau
2013-03-21 19:17:10 UTC
Permalink
Hi Jason,
Post by Jason Ekstrand
Hi Scott,
Post by Scott Moreau
One important thing to note here is that client != surface. In fact,
clients can have multiple surfaces. We might need to keep this in mind
for things like closing single surfaces demonstrated here
https://github.com/soreau/wayland/commit/65f8a3f5f683c3a91913a26496cc373633f01896
Yes, particularly for the closing case.
Post by Scott Moreau
This allows us to tell a client that 'the user has indicated that they
would like one of your surfaces to be closed (this one)'. By way of
contrast, the current code kills the entire client and all of its
surfaces. Unless I am not understanding correctly, we don't have a way
to tell a client to kill only one (or more) of its surfaces with the
current protocol. It might be a good idea to write a test client that
simply has two surfaces from the same client to exercise these cases
(unless there isn't one already). We've been testing with
google-chrome mostly.
This should be taken care of by the adding a "request_close" surface
event (see previous email).
No, actually see Giulio Camuffo's existing patch here
https://github.com/soreau/wayland/commit/65f8a3f5f683c3a91913a26496cc373633f01896
Post by Jason Ekstrand
Post by Scott Moreau
Post by Jason Ekstrand
The point here is that none of this should get
implemented by the client removing the buffer from the surface in
order to unmap it. That's unacceptable. Instead, it should happen in
terms of modes.
1) What do you mean by modes exactly?
2) What would you do instead of placing it from the drawn list to a
dedicated minimized surface list?
This comment merely meant that the client shouldn't minimize a window
by attaching a null buffer. Instead, it should still be a
fully-functioning window (possibly receiving frame events) even though
it's minimized.
I'm not sure why you would ever want to do this, though clients can do
whatever they want.
Post by Jason Ekstrand
Post by Scott Moreau
Post by Jason Ekstrand
There is one more question that I think needs to be answered. And
that is: do we handle things in terms of set/unset or in terms of
set_maximized, set_fullscreen, set_minimized, and set_normal (probably
want a better name for that one). Really, which of those two we do
doesn't matter that much because the toolkit can force it either way.
It's mostly a matter of who tracks the state and handles it. I think
I like simply setting the state instead of keeping track of set/unset
better, but I'm open to discussion on that.
I have this all working in gh next. The only thing left to consider
that I can think of is: Do we want to support 'unmaximizing or
unminimizing (or unfullscreening) a surface retains stacking state'.
So basically if there is a bottom-level surface and you state change
it then toggle it back, do we always want it on top no matter what? Or
do we want to optionally support retaining stacking order on state
restore (setting back to 'normal'). If we want to support this
feature, then a new un* request is required for each state set
request. I move that we do support this feature and I'm working on
this in gh next.
I think stacking should be an orthogonal interface. If we want client
control for it, we need an interface. Otherwise, it should be left as
an implementation detail. I don't think we want to dirty the min/max
protocol with stacking details.
Alright, you do the one for no stacking. I think I'm going to see how
the with-stacking pans out. It seems like an interesting feature to
me.
Post by Jason Ekstrand
Post by Scott Moreau
One other question, do we want to support fullscreen
from a source other than the client? For instance we could have
fullscreen as a selection in the drop down menu? I guess maximize and
minimize are expected features, fullscreen is optional. For this, we'd
need (un)fullscreen events. Hm, I wonder if there's a way to have the
client tell us what states it actually supports so we can correctly
represent this in the panel taskbar menus..
There's no precedent for the compositor full-screening things. Also,
unless the client is specifically designed to full-screen, you won't
be able to get out of it. For these two reasons, I think we should
leave that entirely up to the client.
You can always get out of fullscreen with Alt+Tab IIRC. (but what
happens if you're accepting raw input in the fullscreen client?).
Post by Jason Ekstrand
Post by Scott Moreau
After looking at the code in a working state, it's far clearer to have
explicit $state and un$state events/request because there are a lot of
paths in the code we have to run through to make this all syn up and
work out properly. Using a state variable to simplify the protocol
will likely complicate the description in the protocol and complicate
the code.
Ok, I think you completely misunderstood what I was trying to say
here. My point is that min/max is a state machine. A window, from
the user's perspective, is minimized, maximized, fullscreen, or
normal. It is never two of those at the same time. My question was
about whether, from a protocol perspective, it should be handled in
terms of setting/unsetting one flag per potential state or whether the
client should simply tell the compositor what state to go to next.
For example, if the window is maximized and you click the unmaximize
button, the client will send the set_normal request instead of
unset_maximized.
I explained this. We need the unset request if we want to support
sacking order preservation on state restoration.
Post by Jason Ekstrand
This approach has a number of benefits. First, it simplifies the
protocol: fewer requests. Second, it removes most of the state from
the compositor and lets the toolkits deal with it. This is especially
useful when the toolkit may be messing with other windows than the
current one; in that case, the toolkit would have to keep track of all
that state itself anyway. Also, it removes any ambiguity as to what
is going on inside the compositor and keeps the client in control; the
client always knows the window's state.
As a note on set_toplevel. We need to do some thinking here. It came
up on IRC some time back that we may want to have toplevel be a role
(like subsurface) rather than a flag. Now, when we're adding this
max/min jazz might be the time to do that. In that case, I think a
toplevel would be one role and a toplevel surface would have
maximized, minimized, and normal states while full-screen would be its
own role.
This is totally broken in weston shell plugin. Maximize is state not
type. Toplevel is state not type. Yet, these variables are in contrast
to each other. Just, you can't be toplevel and maximized at the same
time in this context but state is state mot type. See
http://lists.freedesktop.org/archives/wayland-devel/2012-November/006086.html
Post by Jason Ekstrand
If we're re-arranging things like this we could even do it all with a
single set_state request and an enum. (Just a thought, not sure if I
like it.)
Post by Scott Moreau
1) If we always add protocol to the end, it will likely be incoherent,
unmatched and not very easy to read. I know wl_shell interface is
disposable but for the sake of clarity how wayland protocol versioning
system, I'd like to know what the expected convention is.
2) I noticed that changing the version requires no changes client
side. How is this supposed to work?
3) The protocol semantics were recently changed. When semantics are
changed of existing protocol does this not constitute an interface
version bump?
This 1,2,3 is for anyone who knows to respond, not necessarily you.
Post by Jason Ekstrand
1) They always have to go at the end. Otherwise, older clients will
get confused as to which event you are sending and which request they
are sending.
2) I'm not sure.
3) I'm not sure on some of these things. However, we should only have
to bump the interface version once we've released the new interface.
You snipped my stripped protocol here? Anyway, I shared my thoughts.
It seems you are wanting to keep talking about.. well I covered all
the pertinent points that I'm concerned with already. Thanks for your
input.


- Scott
Pekka Paalanen
2013-03-22 09:10:27 UTC
Permalink
On Wed, 20 Mar 2013 22:06:22 -0600
Post by Scott Moreau
Hi Jason,
Post by Jason Ekstrand
There is one more question that I think needs to be answered. And
that is: do we handle things in terms of set/unset or in terms of
set_maximized, set_fullscreen, set_minimized, and set_normal (probably
want a better name for that one). Really, which of those two we do
doesn't matter that much because the toolkit can force it either way.
It's mostly a matter of who tracks the state and handles it. I think
I like simply setting the state instead of keeping track of set/unset
better, but I'm open to discussion on that.
I have this all working in gh next. The only thing left to consider
that I can think of is: Do we want to support 'unmaximizing or
unminimizing (or unfullscreening) a surface retains stacking state'.
So basically if there is a bottom-level surface and you state change
it then toggle it back, do we always want it on top no matter what? Or
do we want to optionally support retaining stacking order on state
restore (setting back to 'normal'). If we want to support this
feature, then a new un* request is required for each state set
request. I move that we do support this feature and I'm working on
this in gh next.
Scott,

do you mean that these unminimize, unmaximize, etc. requests would
actually work like undo? Unmaximize would undo what the last
maximization did, as opposed to just set_normal which might do
something slightly different since its aim is to make the window
normal, not undo something?

If so, does it make sense to have state specific undo requests?

Consider this sequence, driven by the client:
1. the window is normal
2. maximize the window
3. minimize the window
4. unmaximize the window

What happens at step 4? Is it possible to define the outcome of
such cases in an intuitive way?

Does it ever make sense to send a unBAR when the previous operation
was FOO, not BAR? Could you do with just one undo request for all
the un* cases? (If not, why?)

What if a client undoes twice? N times?

Add this to the above sequence:
5. unminimize the window

Is the window now in normal state or maximized state?


I'm thinking this purely from the compositor point of view, and I
don't have any tangible suggestions here, but the above just
seems to generate more illegal than legal sequences, which also
means the code in the compositor must be checking for all the
illegal cases, and emit errors. The illegal sequences might not
make any sense to use, but the compositor (and the protocol spec)
must be aware of what happens when they are received. It might be
worth to actually draw (as on paper) the state machine with all the
requests you might get in each state.

Is there any way this protocol could be designed in a way, that
illegal sequences would not exist, or at least would be in the
minority?

As such, having only the set-requests without corresponding
un-requests would cut down the number of illegal (or just
unintuitive) sequences a lot. Adding a single undo request won't make
it much worse, the only suspicious case is undoing multiple times
in a row, I think. Adding corresponding un-request for each state
request leads to a minor combinatorial explosion of possible
sequences for which there is no obvious idea on what should happen.

Btw. how do you intend to restore the stacking order on undo, in
practice, in the Weston implementation? It is possible other
windows have been deleted, created, and shuffled between set and
undo, so what will you use as the anchor to go back to?


As for the whole idea of undoing stacking order changes; you seem
to assume that set_<state> requests will change the stacking order.
Is that right?

Or is that just a convenient workaround for the fact, that we do
not have protocol for explicitly controlling stacking order? So you
just add implicit stacking side-effects to unrelated requests?

If we had orthogonal requests for controlling stacking order, then
set_<state> requests would not need to touch stacking order at all.
Excluding stacking order, is there something else you would want to
undo, or would the whole undo thing become unneeded?

I would like to strongly suggest to consider splitting the protocol
into orthogonal concepts. That is what I did when I separated
clipping and scaling from the sub-surfaces protocol. It may seem
like more work to start with, but the end result will be cleaner,
more intuitive, and more versatile. It will also allow you to
reduce the interactions and implications, making designing the
protocol easier, and leading to a better end result. The short-term
downside is that you cannot take shortcuts in the design to have
a certain use case running sooner; you will have all use cases
running correctly later. As a compromise to allow development and
testing, your implementation can violate your own spec while
unreleased.

Window state and stacking order often change hand-to-hand, but I
see no reason to tie them together on the protocol level. That is
why I would suggest to handle window state in one set of requests,
and stacking order in a another disjoint set of requests. Moving a
fullscreen window to the top could still be implemented by moving
it to the fullscreen layer in Weston, but that really is just an
implementation detail. From the client's point of view the
fullscreen window is simply "on top" at that time.


Thanks,
pq
Scott Moreau
2013-03-22 23:29:24 UTC
Permalink
Hi Pekka, thanks for your comments here.
Post by Jason Ekstrand
Scott,
do you mean that these unminimize, unmaximize, etc. requests would
actually work like undo? Unmaximize would undo what the last
maximization did, as opposed to just set_normal which might do
something slightly different since its aim is to make the window
normal, not undo something?
Right, I am asking if there is a case for such functionality. The more
I think about it, it seems like it might seem like a bug in normal
desktop usage. But if you happen to have another external program that
you might want to use to manipulate window states, maybe you'd want a
finer grain control there. I'm not sure of all the possible cases,
which is why I'm asking for input.
Post by Jason Ekstrand
If so, does it make sense to have state specific undo requests?
1. the window is normal
2. maximize the window
3. minimize the window
4. unmaximize the window
What happens at step 4? Is it possible to define the outcome of
such cases in an intuitive way?
This illustrates the point perfectly. This is definitely all about
semantics and expected behavior. This must be clearly documented in
the protocol description. I had it working so you could do this. You
could minimize, toggle maximized state and unmaximize with the correct
state. This works fine when doing
maximize->minimize->unmaximize->unminimize but causes a frame glitch
when doing unmaximize->minimize->maximize->unminimize. The frame
glitch is probably easily worked around. However, it is noteworthy
that if you have this behavior, then you can't assume that maximize
and toplevel will be counterparts, but instead, that you'd need
explicit unmaximize notification.
Post by Jason Ekstrand
Does it ever make sense to send a unBAR when the previous operation
was FOO, not BAR? Could you do with just one undo request for all
the un* cases? (If not, why?)
What if a client undoes twice? N times?
5. unminimize the window
Is the window now in normal state or maximized state?
Since this email, I have made it so you cannot toggle maximize while
in a minimized state. This is the way xfce works and it's reasonably
sane behavior.
Post by Jason Ekstrand
I'm thinking this purely from the compositor point of view, and I
don't have any tangible suggestions here, but the above just
seems to generate more illegal than legal sequences, which also
means the code in the compositor must be checking for all the
illegal cases, and emit errors. The illegal sequences might not
make any sense to use, but the compositor (and the protocol spec)
must be aware of what happens when they are received. It might be
worth to actually draw (as on paper) the state machine with all the
requests you might get in each state.
Is there any way this protocol could be designed in a way, that
illegal sequences would not exist, or at least would be in the
minority?
I would like to dismiss the term 'illegal' here in it's entirety
because it is biased by definition. I prefer 'possibly problematic'.
Post by Jason Ekstrand
As such, having only the set-requests without corresponding
un-requests would cut down the number of illegal (or just
unintuitive) sequences a lot. Adding a single undo request won't make
it much worse, the only suspicious case is undoing multiple times
in a row, I think. Adding corresponding un-request for each state
request leads to a minor combinatorial explosion of possible
sequences for which there is no obvious idea on what should happen.
Yes, this is an orchestration between the clients and shell. It
doesn't sound too complicated until you take a look at what's actually
going on. I did not have time to draw a picture but mainly, you have
to keep everyone in sync. And by everyone I mean:

1) The shell plugin
2) The wl_shell_surface clients
3) The xwayland clients
4) The desktop-shell client

These all must be 'on board', for everything to go as intended.
Post by Jason Ekstrand
Btw. how do you intend to restore the stacking order on undo, in
practice, in the Weston implementation? It is possible other
windows have been deleted, created, and shuffled between set and
undo, so what will you use as the anchor to go back to?
I am using gh next as a testbed to work out many of the details.
Post by Jason Ekstrand
As for the whole idea of undoing stacking order changes; you seem
to assume that set_<state> requests will change the stacking order.
Is that right?
The stacking order is (optionally) only changed when a state is restored.
Post by Jason Ekstrand
Or is that just a convenient workaround for the fact, that we do
not have protocol for explicitly controlling stacking order? So you
just add implicit stacking side-effects to unrelated requests?
There's a lot of missing protocol, again, gh next is the testbed for
the current implementation I have.
Post by Jason Ekstrand
If we had orthogonal requests for controlling stacking order, then
set_<state> requests would not need to touch stacking order at all.
Excluding stacking order, is there something else you would want to
undo, or would the whole undo thing become unneeded?
I would like to think, that there are far and few between cases where
we'd want such a behavior. Weston is reportedly a toy, not a real DE
and I'm playing with it a bit. I'm not trying to enforce policy, I'm
trying to open up the possibility of doing more interesting things.
Post by Jason Ekstrand
I would like to strongly suggest to consider splitting the protocol
into orthogonal concepts. That is what I did when I separated
clipping and scaling from the sub-surfaces protocol. It may seem
like more work to start with, but the end result will be cleaner,
more intuitive, and more versatile. It will also allow you to
reduce the interactions and implications, making designing the
protocol easier, and leading to a better end result. The short-term
downside is that you cannot take shortcuts in the design to have
a certain use case running sooner; you will have all use cases
running correctly later. As a compromise to allow development and
testing, your implementation can violate your own spec while
unreleased.
I'm not sure what you mean by 'splitting the protocol into orthogonal
concepts' here.
Post by Jason Ekstrand
Window state and stacking order often change hand-to-hand, but I
see no reason to tie them together on the protocol level. That is
why I would suggest to handle window state in one set of requests,
and stacking order in a another disjoint set of requests. Moving a
fullscreen window to the top could still be implemented by moving
it to the fullscreen layer in Weston, but that really is just an
implementation detail. From the client's point of view the
fullscreen window is simply "on top" at that time.
The bottom line is, the four components I mentioned here, must work
together and be 'on the same page' regarding semantics. Each component
is responsible for it's own state tracking. I have found it is easier
to track state when the calls are in consistent pairs. This also
yields potentially more flexible control for the window manager (shell
plugin).
Post by Jason Ekstrand
Thanks,
pq
- Scott
Bill Spitzak
2013-03-23 01:09:13 UTC
Permalink
The underlying problem is that if a window is full-screen or maximized,
and you minimize it, then un-minimize should put it back to full-screen
or maximized. Thus un-minimize cannot be the "normal" state.

The compositor could track the previous state and set that but then the
client can't change the previous state while minimized.

The proposed "un-minimize request" means the compositor does not know
what state will result after the un-minimize.

I think this can be solved by merging all these states into a single
integer as bits:

MAXIMIZED = 1;
FULL_SCREEN = 2;
MINIMIZED = 4;
...

The rules are that if MINIMIZED is on then it is minimized, and the
other bits are ignored. If not then FULL_SCREEN is on it is fullscreen
and MAXIMIZED is ignored. Etc.

The compositor can send an event to a surface saying that the state
should change to a new set of values. Un-minimize just means it turns
off the minimize bit and sends the new value.

The client can send commands changing the state of surfaces. If it wants
to turn off maximize while minimized, it just turns off the bit.

This has a number of advantages:

1. greatly reduces the api count

2. Easy to add some new "state" to existing shell api

3. All plausable implementations of clients and shells I can think of
would end up storing a set of flags just like this anyway and tracking
the state.
Scott Moreau
2013-03-23 03:04:20 UTC
Permalink
The underlying problem is that if a window is full-screen or maximized, and
you minimize it, then un-minimize should put it back to full-screen or
maximized. Thus un-minimize cannot be the "normal" state.
The compositor could track the previous state and set that but then the
client can't change the previous state while minimized.
The proposed "un-minimize request" means the compositor does not know what
state will result after the un-minimize.
I think this can be solved by merging all these states into a single integer
MAXIMIZED = 1;
FULL_SCREEN = 2;
MINIMIZED = 4;
...
The rules are that if MINIMIZED is on then it is minimized, and the other
bits are ignored. If not then FULL_SCREEN is on it is fullscreen and
MAXIMIZED is ignored. Etc.
The compositor can send an event to a surface saying that the state should
change to a new set of values. Un-minimize just means it turns off the
minimize bit and sends the new value.
The client can send commands changing the state of surfaces. If it wants to
turn off maximize while minimized, it just turns off the bit.
1. greatly reduces the api count
2. Easy to add some new "state" to existing shell api
3. All plausable implementations of clients and shells I can think of would
end up storing a set of flags just like this anyway and tracking the state.
I don't know what your problem is but you simply do not read or think
about anything you type, do you? You are wasting people's time here,
please find something better to do with yours. I am not going to be
using a single semantic you state because you're assumptions make it
sound like your word is absolute when it is in fact frivolous and
largely unnecessary. If you want it done your way, then write it
yourself.
Jason Ekstrand
2013-03-23 20:58:29 UTC
Permalink
Scott,
I have a few technical comments to make down below. Before I go onto
those, I want to make sure we are perfectly clear about the purpose of
this discussion. So please read the following through (multiple times
if needed) before going on to the technical bits.

The Wayland project is primarily about PROTOCOL. Specifically, it is
about the Wayland core protocol: that which is described in
wayland/protocol/wayland.xml. What we (myself, Bill, and Pekka) are
trying to discuss with you is a proposed min/max PROTOCOL that will,
eventually, get added to wayland/protocol/wayland.xml. This has a
number of implications.

First, we are talking about what the protocol OUGHT to be, not what it
is right now. Ideas that get thrown around in the mailing list need
to be evaluated and discussed based on their technical merits and how
clean they make the client/compositor interactions. We need to be
looking for the best possible solution to the problem as a whole. In
particular this means that "I have it working" is not a valid
justification in the face of technical issues or potentially better
solutions.

Second, none of this is yet in wayland master. Until the min/max
protocol makes its way into a Wayland release, nothing is final and
everything is flexible. This means that the entire min/max protocol
is up for revision. Just because you "have it working" in your gh
next branch doesn't mean that it can't be thrown out and re-worked.
Also, It is not practical for us to hold a discussion based on the
incremental changes that you commit to your gh next branch. As the
protocol gets re-worked, please send new versions to the list, rebased
against wayland master, that incorporate those changes. This will
make it much easier to see how everything fits into the Wayland
protocol as a whole. (I already asked you to do this a few e-mails
ago.)

Third, this discussion is NOT about implementation. While Weston and
your gh next Weston may be valuable, they are largely irrelevant to
the current discussion. What we need to focus on is trying to make
the client/compositor interactions as clean as possible. This has
nothing to do with the internal implementation details of Weston or
any other compositor. Those implementation details are useful only in
so far as they help us understand the client/compositor interactions
and the potential pitfalls of a given protocol.

Finally, this is about the Wayland protocol, not the Weston protocol.
This means that we don't just throw a bunch of undocumented
events/requests into the protocol file, start implementing it in
Weston, and document how we ended up implementing it. That is exactly
backwards of how the protocol should be developed. I'm not saying
that implementing it in Weston isn't useful for getting the kinks out.
What I'm saying is that the protocol comes first and then we
implement it to make sure it works.

One more thing before I go onto the technical details: Bill Spitzak.
Just because he gets on your nerves doesn't mean you should just
ignore him. You are very much into the implementation details whereas
Bill tends to come at things from a perspective that is more
high-level and theoretical. This is a very useful perspective when
discussing a protocol because the protocol should transcend the
details as much as possible. Bill does read the e-mails and he
frequently points out potential pitfalls that other people miss. He
is also usually very clear in his examples (in his first e-mail in
this series he drew you an ASCII-art picture to demonstrate what he
meant). You need to stop ignoring him and writing off everything he
says as worthless. At the very least you need to act towards him in a
more professional manner.

Technical comments follow.
Post by Scott Moreau
Hi Pekka, thanks for your comments here.
Post by Jason Ekstrand
Scott,
do you mean that these unminimize, unmaximize, etc. requests would
actually work like undo? Unmaximize would undo what the last
maximization did, as opposed to just set_normal which might do
something slightly different since its aim is to make the window
normal, not undo something?
Right, I am asking if there is a case for such functionality. The more
I think about it, it seems like it might seem like a bug in normal
desktop usage. But if you happen to have another external program that
you might want to use to manipulate window states, maybe you'd want a
finer grain control there. I'm not sure of all the possible cases,
which is why I'm asking for input.
Post by Jason Ekstrand
If so, does it make sense to have state specific undo requests?
1. the window is normal
2. maximize the window
3. minimize the window
4. unmaximize the window
What happens at step 4? Is it possible to define the outcome of
such cases in an intuitive way?
This illustrates the point perfectly. This is definitely all about
semantics and expected behavior. This must be clearly documented in
the protocol description. I had it working so you could do this. You
could minimize, toggle maximized state and unmaximize with the correct
state. This works fine when doing
maximize->minimize->unmaximize->unminimize but causes a frame glitch
when doing unmaximize->minimize->maximize->unminimize. The frame
glitch is probably easily worked around. However, it is noteworthy
that if you have this behavior, then you can't assume that maximize
and toplevel will be counterparts, but instead, that you'd need
explicit unmaximize notification.
The last thing we want is a protocol full of "semantics and expected
behaviour". If implementing this correctly requires a lot of expected
behaviour, we need to re-think the protocol.

To solve this problem I suggested above that we simply replace all of
the set_X and unset_X requests with set_X requests and one
"set_normal" or whatever that returns to a default window state. This
way, instead of having to worry about what to set/unset to get to a
desired state, the client simply tells the compositor what the next
state should be. In order to do things correctly, the client is going
to have to track state anyway, so it might as well explicitly tell the
compositor what state it wants next. (This suggestion has gone
completely unanswered.)
Post by Scott Moreau
Post by Jason Ekstrand
Does it ever make sense to send a unBAR when the previous operation
was FOO, not BAR? Could you do with just one undo request for all
the un* cases? (If not, why?)
What if a client undoes twice? N times?
5. unminimize the window
Is the window now in normal state or maximized state?
Since this email, I have made it so you cannot toggle maximize while
in a minimized state. This is the way xfce works and it's reasonably
sane behavior.
Post by Jason Ekstrand
I'm thinking this purely from the compositor point of view, and I
don't have any tangible suggestions here, but the above just
seems to generate more illegal than legal sequences, which also
means the code in the compositor must be checking for all the
illegal cases, and emit errors. The illegal sequences might not
make any sense to use, but the compositor (and the protocol spec)
must be aware of what happens when they are received. It might be
worth to actually draw (as on paper) the state machine with all the
requests you might get in each state.
Is there any way this protocol could be designed in a way, that
illegal sequences would not exist, or at least would be in the
minority?
I would like to dismiss the term 'illegal' here in it's entirety
because it is biased by definition. I prefer 'possibly problematic'.
When talking about a protocol "illegal" is a perfectly valid word.
Until we define how everything should work there's a significant
possibility for "illegal" interactions.
Post by Scott Moreau
Post by Jason Ekstrand
As such, having only the set-requests without corresponding
un-requests would cut down the number of illegal (or just
unintuitive) sequences a lot. Adding a single undo request won't make
it much worse, the only suspicious case is undoing multiple times
in a row, I think. Adding corresponding un-request for each state
request leads to a minor combinatorial explosion of possible
sequences for which there is no obvious idea on what should happen.
Yes, this is an orchestration between the clients and shell. It
doesn't sound too complicated until you take a look at what's actually
going on. I did not have time to draw a picture but mainly, you have
1) The shell plugin
2) The wl_shell_surface clients
3) The xwayland clients
4) The desktop-shell client
These all must be 'on board', for everything to go as intended.
This is entirely an implementation detail of weston. It is completely
irrelevant to the current discussion.
Post by Scott Moreau
Post by Jason Ekstrand
Btw. how do you intend to restore the stacking order on undo, in
practice, in the Weston implementation? It is possible other
windows have been deleted, created, and shuffled between set and
undo, so what will you use as the anchor to go back to?
I am using gh next as a testbed to work out many of the details.
Post by Jason Ekstrand
As for the whole idea of undoing stacking order changes; you seem
to assume that set_<state> requests will change the stacking order.
Is that right?
The stacking order is (optionally) only changed when a state is restored.
Post by Jason Ekstrand
Or is that just a convenient workaround for the fact, that we do
not have protocol for explicitly controlling stacking order? So you
just add implicit stacking side-effects to unrelated requests?
There's a lot of missing protocol, again, gh next is the testbed for
the current implementation I have.
Post by Jason Ekstrand
If we had orthogonal requests for controlling stacking order, then
set_<state> requests would not need to touch stacking order at all.
Excluding stacking order, is there something else you would want to
undo, or would the whole undo thing become unneeded?
I would like to think, that there are far and few between cases where
we'd want such a behavior. Weston is reportedly a toy, not a real DE
and I'm playing with it a bit. I'm not trying to enforce policy, I'm
trying to open up the possibility of doing more interesting things.
Post by Jason Ekstrand
I would like to strongly suggest to consider splitting the protocol
into orthogonal concepts. That is what I did when I separated
clipping and scaling from the sub-surfaces protocol. It may seem
like more work to start with, but the end result will be cleaner,
more intuitive, and more versatile. It will also allow you to
reduce the interactions and implications, making designing the
protocol easier, and leading to a better end result. The short-term
downside is that you cannot take shortcuts in the design to have
a certain use case running sooner; you will have all use cases
running correctly later. As a compromise to allow development and
testing, your implementation can violate your own spec while
unreleased.
I'm not sure what you mean by 'splitting the protocol into orthogonal
concepts' here.
By "splitting into orthogonal concepts" he means developing two
completely separate protocols: one for specifying stacking order, the
other for minimizing/maximizing. At the protocol level, they would
have basically no overlap. In particular, the min/max protocol should
specify NOTHING about stacking order other than, perhaps, fullscreen
is on top.
Post by Scott Moreau
Post by Jason Ekstrand
Window state and stacking order often change hand-to-hand, but I
see no reason to tie them together on the protocol level. That is
why I would suggest to handle window state in one set of requests,
and stacking order in a another disjoint set of requests. Moving a
fullscreen window to the top could still be implemented by moving
it to the fullscreen layer in Weston, but that really is just an
implementation detail. From the client's point of view the
fullscreen window is simply "on top" at that time.
The bottom line is, the four components I mentioned here, must work
together and be 'on the same page' regarding semantics. Each component
is responsible for it's own state tracking. I have found it is easier
to track state when the calls are in consistent pairs. This also
yields potentially more flexible control for the window manager (shell
plugin).
I don't see how this comment has anything to do with the interaction
between stacking and min/max. Also, it's mostly about implementation
details again.
Post by Scott Moreau
The underlying problem is that if a window is full-screen or maximized, and
you minimize it, then un-minimize should put it back to full-screen or
maximized. Thus un-minimize cannot be the "normal" state.
The compositor could track the previous state and set that but then the
client can't change the previous state while minimized.
The proposed "un-minimize request" means the compositor does not know what
state will result after the un-minimize.
I think this can be solved by merging all these states into a single integer
MAXIMIZED = 1;
FULL_SCREEN = 2;
MINIMIZED = 4;
...
The rules are that if MINIMIZED is on then it is minimized, and the other
bits are ignored. If not then FULL_SCREEN is on it is fullscreen and
MAXIMIZED is ignored. Etc.
Yes, If we're going to handle min/max in terms of setting/unsetting
flags, we definitely need a precedence order. There are other ways
that you could probably describe what to do with each given
combination. However, I think simply setting a precedence order is
the cleanest and easiest to get consistent.
Post by Scott Moreau
The compositor can send an event to a surface saying that the state should
change to a new set of values. Un-minimize just means it turns off the
minimize bit and sends the new value.
The client can send commands changing the state of surfaces. If it wants to
turn off maximize while minimized, it just turns off the bit.
1. greatly reduces the api count
2. Easy to add some new "state" to existing shell api
3. All plausable implementations of clients and shells I can think of would
end up storing a set of flags just like this anyway and tracking the state.
Thanks for your comments Bill! I have thought about this solution and
I think it would work fairly well if we were to have the client send
an explicit next state such as minimized, maximized, or normal to the
compositor. However, I'm not sure I like it for the flags. For one
thing, it implies a precedence order which is good until we add a flag
whose precedence is somewhere in the middle. Second, it allows the
client to flip any number of flags before sending them to the
compositor. At that point, it might as well just send the final state
and forget the flags.

One more thing: Could you please send out a version 2 with whatever
changes have been made since the original version you sent to the
list. This will greatly help with the discussion.

Thanks,
--Jason Ekstrand
Scott Moreau
2013-03-23 23:10:29 UTC
Permalink
Hi Jason,
Post by Jason Ekstrand
Scott,
I have a few technical comments to make down below. Before I go onto
those, I want to make sure we are perfectly clear about the purpose of
this discussion. So please read the following through (multiple times
if needed) before going on to the technical bits.
Yes, I'm glad you brought up the purpose of this discussion.
Ultimately, I don't think we all share the same general purpose.
Post by Jason Ekstrand
The Wayland project is primarily about PROTOCOL. Specifically, it is
about the Wayland core protocol: that which is described in
wayland/protocol/wayland.xml.
I am curious, is the primary purpose or ultimate goals of wayland
documented somewhere? Kristian?
Post by Jason Ekstrand
What we (myself, Bill, and Pekka) are
trying to discuss with you is a proposed min/max PROTOCOL that will,
eventually, get added to wayland/protocol/wayland.xml. This has a
number of implications.
I've had these patches on the list open for discussion for five months
now, give or take. During this time, the window list was briefly
reviewed by Kristian yet, none of the core protocol commented on. This
was surprising to me but I assumed since the window list patch was
reviewed, silence on the protocol implied acceptance. Unfortunately,
during this time some things have also changed and I have come to many
realizations.
Post by Jason Ekstrand
First, we are talking about what the protocol OUGHT to be, not what it
is right now. Ideas that get thrown around in the mailing list need
to be evaluated and discussed based on their technical merits and how
clean they make the client/compositor interactions. We need to be
looking for the best possible solution to the problem as a whole. In
particular this means that "I have it working" is not a valid
justification in the face of technical issues or potentially better
solutions.
Yes, you are right that the protocol should serve the greater good as
a whole. "I have it working" means the same thing from any weston
component. Sure, things work but this does not mean that the
implementations upstream are perfect. There are plenty of bugs in
weston as well as my implementations. The point is, that once you see
the code in a 'working' state, this provides more insight as to what
might be good things to look out for, for future cases. Anyone can
talk and have ideas but if the implementation cannot be worked out as
discussed in the code for technical reasons, naturally adjustments
need to be made. "I have working" does not mean "My implementation is
the only correct one and only one that should exist" which you seem to
be misunderstanding.
Post by Jason Ekstrand
Second, none of this is yet in wayland master. Until the min/max
protocol makes its way into a Wayland release, nothing is final and
everything is flexible. This means that the entire min/max protocol
is up for revision. Just because you "have it working" in your gh
next branch doesn't mean that it can't be thrown out and re-worked.
Also, It is not practical for us to hold a discussion based on the
incremental changes that you commit to your gh next branch. As the
protocol gets re-worked, please send new versions to the list, rebased
against wayland master, that incorporate those changes. This will
make it much easier to see how everything fits into the Wayland
protocol as a whole. (I already asked you to do this a few e-mails
ago.)
Alright, now you're moving into an assumption that wayland is and will
be the only protocol to use the new driver infrastructure and it will
be absolute. This would be nice but it's not likely going to be the
case as time progresses. See the ever-so-(un)popular Mir
implementation. People are humans that have desires and wants, not a
plugin to the wayland protocol. In order to attempt considering all
scenarios, you might expand your thoughts outside of the wayland
realm. For the purposes of this discussion, you can stay isolated to
your wayland box ideals. I for myself, will continue to strive for
things that makes sense to myself and others.
Post by Jason Ekstrand
Third, this discussion is NOT about implementation. While Weston and
your gh next Weston may be valuable, they are largely irrelevant to
the current discussion. What we need to focus on is trying to make
the client/compositor interactions as clean as possible. This has
nothing to do with the internal implementation details of Weston or
any other compositor. Those implementation details are useful only in
so far as they help us understand the client/compositor interactions
and the potential pitfalls of a given protocol.
I have given my representations of what I think is a clean solution.
The implementation must follow protocol but beyond that isn't
extremely important. However, it has been the policy in wayland/weston
to only push protocol with working implementations in the official
reference compositor, weston. You are right that a wayland compositor
implementation isn't specifically important to the wayland protocol
but specifically, the implementation in weston is in fact important.
Again, not saying my solution is correct or absolute in any way, just
that we can't say 'let us make the protocol first, push it and then
figure out how or if it works, later'
Post by Jason Ekstrand
Finally, this is about the Wayland protocol, not the Weston protocol.
This means that we don't just throw a bunch of undocumented
events/requests into the protocol file, start implementing it in
Weston, and document how we ended up implementing it. That is exactly
backwards of how the protocol should be developed. I'm not saying
that implementing it in Weston isn't useful for getting the kinks out.
What I'm saying is that the protocol comes first and then we
implement it to make sure it works.
I think that you are completely wrong here. We *definitely* want to
get new protocol in place and try it out. The policy of wayland/weston
has been, that when new protocol is added, the example implementations
are added to weston and other relevant components. As I explained, the
min/max descriptions were intentionally and thoughtfully left blank
because there is a whole lot involved for the expected desktop cases
and it's a touchy subject too. However, you don't have to overthink
this. The best solution is to add protocol for everyone, not just
isolating to the immediate use cases that you see and disregard
everyone elses ideas as unneeded/crazy.
Post by Jason Ekstrand
One more thing before I go onto the technical details: Bill Spitzak.
Just because he gets on your nerves doesn't mean you should just
ignore him. You are very much into the implementation details whereas
Bill tends to come at things from a perspective that is more
high-level and theoretical. This is a very useful perspective when
discussing a protocol because the protocol should transcend the
details as much as possible. Bill does read the e-mails and he
frequently points out potential pitfalls that other people miss. He
is also usually very clear in his examples (in his first e-mail in
this series he drew you an ASCII-art picture to demonstrate what he
meant). You need to stop ignoring him and writing off everything he
says as worthless. At the very least you need to act towards him in a
more professional manner.
Don't care about Bill. He doesn't contribute, he wastes time. You are
beginning to do the same thing by trolling me here. I really don't
appreciate it.
Post by Jason Ekstrand
Technical comments follow.
Post by Scott Moreau
Hi Pekka, thanks for your comments here.
Post by Jason Ekstrand
Scott,
do you mean that these unminimize, unmaximize, etc. requests would
actually work like undo? Unmaximize would undo what the last
maximization did, as opposed to just set_normal which might do
something slightly different since its aim is to make the window
normal, not undo something?
Right, I am asking if there is a case for such functionality. The more
I think about it, it seems like it might seem like a bug in normal
desktop usage. But if you happen to have another external program that
you might want to use to manipulate window states, maybe you'd want a
finer grain control there. I'm not sure of all the possible cases,
which is why I'm asking for input.
Post by Jason Ekstrand
If so, does it make sense to have state specific undo requests?
1. the window is normal
2. maximize the window
3. minimize the window
4. unmaximize the window
What happens at step 4? Is it possible to define the outcome of
such cases in an intuitive way?
This illustrates the point perfectly. This is definitely all about
semantics and expected behavior. This must be clearly documented in
the protocol description. I had it working so you could do this. You
could minimize, toggle maximized state and unmaximize with the correct
state. This works fine when doing
maximize->minimize->unmaximize->unminimize but causes a frame glitch
when doing unmaximize->minimize->maximize->unminimize. The frame
glitch is probably easily worked around. However, it is noteworthy
that if you have this behavior, then you can't assume that maximize
and toplevel will be counterparts, but instead, that you'd need
explicit unmaximize notification.
The last thing we want is a protocol full of "semantics and expected
behaviour". If implementing this correctly requires a lot of expected
behaviour, we need to re-think the protocol.
To solve this problem I suggested above that we simply replace all of
the set_X and unset_X requests with set_X requests and one
"set_normal" or whatever that returns to a default window state. This
way, instead of having to worry about what to set/unset to get to a
desired state, the client simply tells the compositor what the next
state should be. In order to do things correctly, the client is going
to have to track state anyway, so it might as well explicitly tell the
compositor what state it wants next. (This suggestion has gone
completely unanswered.)
Post by Scott Moreau
Post by Jason Ekstrand
Does it ever make sense to send a unBAR when the previous operation
was FOO, not BAR? Could you do with just one undo request for all
the un* cases? (If not, why?)
What if a client undoes twice? N times?
5. unminimize the window
Is the window now in normal state or maximized state?
Since this email, I have made it so you cannot toggle maximize while
in a minimized state. This is the way xfce works and it's reasonably
sane behavior.
Post by Jason Ekstrand
I'm thinking this purely from the compositor point of view, and I
don't have any tangible suggestions here, but the above just
seems to generate more illegal than legal sequences, which also
means the code in the compositor must be checking for all the
illegal cases, and emit errors. The illegal sequences might not
make any sense to use, but the compositor (and the protocol spec)
must be aware of what happens when they are received. It might be
worth to actually draw (as on paper) the state machine with all the
requests you might get in each state.
Is there any way this protocol could be designed in a way, that
illegal sequences would not exist, or at least would be in the
minority?
I would like to dismiss the term 'illegal' here in it's entirety
because it is biased by definition. I prefer 'possibly problematic'.
When talking about a protocol "illegal" is a perfectly valid word.
Until we define how everything should work there's a significant
possibility for "illegal" interactions.
Post by Scott Moreau
Post by Jason Ekstrand
As such, having only the set-requests without corresponding
un-requests would cut down the number of illegal (or just
unintuitive) sequences a lot. Adding a single undo request won't make
it much worse, the only suspicious case is undoing multiple times
in a row, I think. Adding corresponding un-request for each state
request leads to a minor combinatorial explosion of possible
sequences for which there is no obvious idea on what should happen.
Yes, this is an orchestration between the clients and shell. It
doesn't sound too complicated until you take a look at what's actually
going on. I did not have time to draw a picture but mainly, you have
1) The shell plugin
2) The wl_shell_surface clients
3) The xwayland clients
4) The desktop-shell client
These all must be 'on board', for everything to go as intended.
This is entirely an implementation detail of weston. It is completely
irrelevant to the current discussion.
Post by Scott Moreau
Post by Jason Ekstrand
Btw. how do you intend to restore the stacking order on undo, in
practice, in the Weston implementation? It is possible other
windows have been deleted, created, and shuffled between set and
undo, so what will you use as the anchor to go back to?
I am using gh next as a testbed to work out many of the details.
Post by Jason Ekstrand
As for the whole idea of undoing stacking order changes; you seem
to assume that set_<state> requests will change the stacking order.
Is that right?
The stacking order is (optionally) only changed when a state is restored.
Post by Jason Ekstrand
Or is that just a convenient workaround for the fact, that we do
not have protocol for explicitly controlling stacking order? So you
just add implicit stacking side-effects to unrelated requests?
There's a lot of missing protocol, again, gh next is the testbed for
the current implementation I have.
Post by Jason Ekstrand
If we had orthogonal requests for controlling stacking order, then
set_<state> requests would not need to touch stacking order at all.
Excluding stacking order, is there something else you would want to
undo, or would the whole undo thing become unneeded?
I would like to think, that there are far and few between cases where
we'd want such a behavior. Weston is reportedly a toy, not a real DE
and I'm playing with it a bit. I'm not trying to enforce policy, I'm
trying to open up the possibility of doing more interesting things.
Post by Jason Ekstrand
I would like to strongly suggest to consider splitting the protocol
into orthogonal concepts. That is what I did when I separated
clipping and scaling from the sub-surfaces protocol. It may seem
like more work to start with, but the end result will be cleaner,
more intuitive, and more versatile. It will also allow you to
reduce the interactions and implications, making designing the
protocol easier, and leading to a better end result. The short-term
downside is that you cannot take shortcuts in the design to have
a certain use case running sooner; you will have all use cases
running correctly later. As a compromise to allow development and
testing, your implementation can violate your own spec while
unreleased.
I'm not sure what you mean by 'splitting the protocol into orthogonal
concepts' here.
By "splitting into orthogonal concepts" he means developing two
completely separate protocols: one for specifying stacking order, the
other for minimizing/maximizing. At the protocol level, they would
have basically no overlap. In particular, the min/max protocol should
specify NOTHING about stacking order other than, perhaps, fullscreen
is on top.
Post by Scott Moreau
Post by Jason Ekstrand
Window state and stacking order often change hand-to-hand, but I
see no reason to tie them together on the protocol level. That is
why I would suggest to handle window state in one set of requests,
and stacking order in a another disjoint set of requests. Moving a
fullscreen window to the top could still be implemented by moving
it to the fullscreen layer in Weston, but that really is just an
implementation detail. From the client's point of view the
fullscreen window is simply "on top" at that time.
The bottom line is, the four components I mentioned here, must work
together and be 'on the same page' regarding semantics. Each component
is responsible for it's own state tracking. I have found it is easier
to track state when the calls are in consistent pairs. This also
yields potentially more flexible control for the window manager (shell
plugin).
I don't see how this comment has anything to do with the interaction
between stacking and min/max. Also, it's mostly about implementation
details again.
Post by Scott Moreau
The underlying problem is that if a window is full-screen or maximized, and
you minimize it, then un-minimize should put it back to full-screen or
maximized. Thus un-minimize cannot be the "normal" state.
The compositor could track the previous state and set that but then the
client can't change the previous state while minimized.
The proposed "un-minimize request" means the compositor does not know what
state will result after the un-minimize.
I think this can be solved by merging all these states into a single integer
MAXIMIZED = 1;
FULL_SCREEN = 2;
MINIMIZED = 4;
...
The rules are that if MINIMIZED is on then it is minimized, and the other
bits are ignored. If not then FULL_SCREEN is on it is fullscreen and
MAXIMIZED is ignored. Etc.
Yes, If we're going to handle min/max in terms of setting/unsetting
flags, we definitely need a precedence order. There are other ways
that you could probably describe what to do with each given
combination. However, I think simply setting a precedence order is
the cleanest and easiest to get consistent.
Post by Scott Moreau
The compositor can send an event to a surface saying that the state should
change to a new set of values. Un-minimize just means it turns off the
minimize bit and sends the new value.
The client can send commands changing the state of surfaces. If it wants to
turn off maximize while minimized, it just turns off the bit.
1. greatly reduces the api count
2. Easy to add some new "state" to existing shell api
3. All plausable implementations of clients and shells I can think of would
end up storing a set of flags just like this anyway and tracking the state.
Thanks for your comments Bill! I have thought about this solution and
I think it would work fairly well if we were to have the client send
an explicit next state such as minimized, maximized, or normal to the
compositor. However, I'm not sure I like it for the flags. For one
thing, it implies a precedence order which is good until we add a flag
whose precedence is somewhere in the middle. Second, it allows the
client to flip any number of flags before sending them to the
compositor. At that point, it might as well just send the final state
and forget the flags.
One more thing: Could you please send out a version 2 with whatever
changes have been made since the original version you sent to the
list. This will greatly help with the discussion.
Thanks,
--Jason Ekstrand
I don't have time for this, you are trolling by now. You guys can take
and make the protocol however you see best fit for your wayland
project.


Thanks,

Scott
Pekka Paalanen
2013-03-24 10:07:15 UTC
Permalink
On Sat, 23 Mar 2013 15:58:29 -0500
Post by Jason Ekstrand
Scott,
I have a few technical comments to make down below. Before I go onto
those, I want to make sure we are perfectly clear about the purpose of
this discussion. So please read the following through (multiple times
if needed) before going on to the technical bits.
The Wayland project is primarily about PROTOCOL. Specifically, it is
about the Wayland core protocol: that which is described in
wayland/protocol/wayland.xml. What we (myself, Bill, and Pekka) are
trying to discuss with you is a proposed min/max PROTOCOL that will,
eventually, get added to wayland/protocol/wayland.xml. This has a
number of implications.
First, we are talking about what the protocol OUGHT to be, not what it
is right now. Ideas that get thrown around in the mailing list need
to be evaluated and discussed based on their technical merits and how
clean they make the client/compositor interactions. We need to be
looking for the best possible solution to the problem as a whole. In
particular this means that "I have it working" is not a valid
justification in the face of technical issues or potentially better
solutions.
Second, none of this is yet in wayland master. Until the min/max
protocol makes its way into a Wayland release, nothing is final and
everything is flexible. This means that the entire min/max protocol
is up for revision. Just because you "have it working" in your gh
next branch doesn't mean that it can't be thrown out and re-worked.
Also, It is not practical for us to hold a discussion based on the
incremental changes that you commit to your gh next branch. As the
protocol gets re-worked, please send new versions to the list, rebased
against wayland master, that incorporate those changes. This will
make it much easier to see how everything fits into the Wayland
protocol as a whole. (I already asked you to do this a few e-mails
ago.)
Third, this discussion is NOT about implementation. While Weston and
your gh next Weston may be valuable, they are largely irrelevant to
the current discussion. What we need to focus on is trying to make
the client/compositor interactions as clean as possible. This has
nothing to do with the internal implementation details of Weston or
any other compositor. Those implementation details are useful only in
so far as they help us understand the client/compositor interactions
and the potential pitfalls of a given protocol.
Finally, this is about the Wayland protocol, not the Weston protocol.
This means that we don't just throw a bunch of undocumented
events/requests into the protocol file, start implementing it in
Weston, and document how we ended up implementing it. That is exactly
backwards of how the protocol should be developed. I'm not saying
that implementing it in Weston isn't useful for getting the kinks out.
What I'm saying is that the protocol comes first and then we
implement it to make sure it works.
One more thing before I go onto the technical details: Bill Spitzak.
Just because he gets on your nerves doesn't mean you should just
ignore him. You are very much into the implementation details whereas
Bill tends to come at things from a perspective that is more
high-level and theoretical. This is a very useful perspective when
discussing a protocol because the protocol should transcend the
details as much as possible. Bill does read the e-mails and he
frequently points out potential pitfalls that other people miss. He
is also usually very clear in his examples (in his first e-mail in
this series he drew you an ASCII-art picture to demonstrate what he
meant). You need to stop ignoring him and writing off everything he
says as worthless. At the very least you need to act towards him in a
more professional manner.
Technical comments follow.
Post by Scott Moreau
Hi Pekka, thanks for your comments here.
Post by Jason Ekstrand
Scott,
do you mean that these unminimize, unmaximize, etc. requests would
actually work like undo? Unmaximize would undo what the last
maximization did, as opposed to just set_normal which might do
something slightly different since its aim is to make the window
normal, not undo something?
Right, I am asking if there is a case for such functionality. The more
I think about it, it seems like it might seem like a bug in normal
desktop usage. But if you happen to have another external program that
you might want to use to manipulate window states, maybe you'd want a
finer grain control there. I'm not sure of all the possible cases,
which is why I'm asking for input.
Post by Jason Ekstrand
If so, does it make sense to have state specific undo requests?
1. the window is normal
2. maximize the window
3. minimize the window
4. unmaximize the window
What happens at step 4? Is it possible to define the outcome of
such cases in an intuitive way?
This illustrates the point perfectly. This is definitely all about
semantics and expected behavior. This must be clearly documented in
the protocol description. I had it working so you could do this. You
could minimize, toggle maximized state and unmaximize with the correct
state. This works fine when doing
maximize->minimize->unmaximize->unminimize but causes a frame glitch
when doing unmaximize->minimize->maximize->unminimize. The frame
glitch is probably easily worked around. However, it is noteworthy
that if you have this behavior, then you can't assume that maximize
and toplevel will be counterparts, but instead, that you'd need
explicit unmaximize notification.
The last thing we want is a protocol full of "semantics and expected
behaviour". If implementing this correctly requires a lot of expected
behaviour, we need to re-think the protocol.
To solve this problem I suggested above that we simply replace all of
the set_X and unset_X requests with set_X requests and one
"set_normal" or whatever that returns to a default window state. This
way, instead of having to worry about what to set/unset to get to a
desired state, the client simply tells the compositor what the next
state should be. In order to do things correctly, the client is going
to have to track state anyway, so it might as well explicitly tell the
compositor what state it wants next. (This suggestion has gone
completely unanswered.)
Post by Scott Moreau
Post by Jason Ekstrand
Does it ever make sense to send a unBAR when the previous operation
was FOO, not BAR? Could you do with just one undo request for all
the un* cases? (If not, why?)
What if a client undoes twice? N times?
5. unminimize the window
Is the window now in normal state or maximized state?
Since this email, I have made it so you cannot toggle maximize while
in a minimized state. This is the way xfce works and it's reasonably
sane behavior.
Post by Jason Ekstrand
I'm thinking this purely from the compositor point of view, and I
don't have any tangible suggestions here, but the above just
seems to generate more illegal than legal sequences, which also
means the code in the compositor must be checking for all the
illegal cases, and emit errors. The illegal sequences might not
make any sense to use, but the compositor (and the protocol spec)
must be aware of what happens when they are received. It might be
worth to actually draw (as on paper) the state machine with all the
requests you might get in each state.
Is there any way this protocol could be designed in a way, that
illegal sequences would not exist, or at least would be in the
minority?
I would like to dismiss the term 'illegal' here in it's entirety
because it is biased by definition. I prefer 'possibly problematic'.
When talking about a protocol "illegal" is a perfectly valid word.
Until we define how everything should work there's a significant
possibility for "illegal" interactions.
Post by Scott Moreau
Post by Jason Ekstrand
As such, having only the set-requests without corresponding
un-requests would cut down the number of illegal (or just
unintuitive) sequences a lot. Adding a single undo request won't make
it much worse, the only suspicious case is undoing multiple times
in a row, I think. Adding corresponding un-request for each state
request leads to a minor combinatorial explosion of possible
sequences for which there is no obvious idea on what should happen.
Yes, this is an orchestration between the clients and shell. It
doesn't sound too complicated until you take a look at what's actually
going on. I did not have time to draw a picture but mainly, you have
1) The shell plugin
2) The wl_shell_surface clients
3) The xwayland clients
4) The desktop-shell client
These all must be 'on board', for everything to go as intended.
This is entirely an implementation detail of weston. It is completely
irrelevant to the current discussion.
Post by Scott Moreau
Post by Jason Ekstrand
Btw. how do you intend to restore the stacking order on undo, in
practice, in the Weston implementation? It is possible other
windows have been deleted, created, and shuffled between set and
undo, so what will you use as the anchor to go back to?
I am using gh next as a testbed to work out many of the details.
Post by Jason Ekstrand
As for the whole idea of undoing stacking order changes; you seem
to assume that set_<state> requests will change the stacking order.
Is that right?
The stacking order is (optionally) only changed when a state is restored.
Post by Jason Ekstrand
Or is that just a convenient workaround for the fact, that we do
not have protocol for explicitly controlling stacking order? So you
just add implicit stacking side-effects to unrelated requests?
There's a lot of missing protocol, again, gh next is the testbed for
the current implementation I have.
Post by Jason Ekstrand
If we had orthogonal requests for controlling stacking order, then
set_<state> requests would not need to touch stacking order at all.
Excluding stacking order, is there something else you would want to
undo, or would the whole undo thing become unneeded?
I would like to think, that there are far and few between cases where
we'd want such a behavior. Weston is reportedly a toy, not a real DE
and I'm playing with it a bit. I'm not trying to enforce policy, I'm
trying to open up the possibility of doing more interesting things.
Post by Jason Ekstrand
I would like to strongly suggest to consider splitting the protocol
into orthogonal concepts. That is what I did when I separated
clipping and scaling from the sub-surfaces protocol. It may seem
like more work to start with, but the end result will be cleaner,
more intuitive, and more versatile. It will also allow you to
reduce the interactions and implications, making designing the
protocol easier, and leading to a better end result. The short-term
downside is that you cannot take shortcuts in the design to have
a certain use case running sooner; you will have all use cases
running correctly later. As a compromise to allow development and
testing, your implementation can violate your own spec while
unreleased.
I'm not sure what you mean by 'splitting the protocol into orthogonal
concepts' here.
By "splitting into orthogonal concepts" he means developing two
completely separate protocols: one for specifying stacking order, the
other for minimizing/maximizing. At the protocol level, they would
have basically no overlap. In particular, the min/max protocol should
specify NOTHING about stacking order other than, perhaps, fullscreen
is on top.
Post by Scott Moreau
Post by Jason Ekstrand
Window state and stacking order often change hand-to-hand, but I
see no reason to tie them together on the protocol level. That is
why I would suggest to handle window state in one set of requests,
and stacking order in a another disjoint set of requests. Moving a
fullscreen window to the top could still be implemented by moving
it to the fullscreen layer in Weston, but that really is just an
implementation detail. From the client's point of view the
fullscreen window is simply "on top" at that time.
The bottom line is, the four components I mentioned here, must work
together and be 'on the same page' regarding semantics. Each component
is responsible for it's own state tracking. I have found it is easier
to track state when the calls are in consistent pairs. This also
yields potentially more flexible control for the window manager (shell
plugin).
I don't see how this comment has anything to do with the interaction
between stacking and min/max. Also, it's mostly about implementation
details again.
Jason,

thanks for reading my mind and replying what I wanted to say, but
was too tired. :-)
- pq
Bill Spitzak
2013-03-25 17:24:57 UTC
Permalink
Post by Jason Ekstrand
One more thing before I go onto the technical details: Bill Spitzak.
Just because he gets on your nerves doesn't mean you should just
ignore him.
Thanks but I think my email was pretty rambling and I should not have
sent it. Can try to say what I wanted in a *short* way:

A surface can be both "minimized" and "maximized" at the same time, ie
it looks minimized but when un-minimized it then looks maximized. I
thought it might be necessary for both the client and the compositor to
know this, and thus a set of on/off flags sent as a batch would be the
best way to communicate this. A sequence of on/off command for each flag
would have annoying bugs if the two ever got out of sync.
Post by Jason Ekstrand
Thanks for your comments Bill! I have thought about this solution and
I think it would work fairly well if we were to have the client send
an explicit next state such as minimized, maximized, or normal to the
compositor.
I think you may be right that the compositor does not actually need to
know the set of states, so there is no problem with bugs making the
compositor and client disagree, which was the primary reason I thought
sending the entire set of states at once was a good idea.

This will require the compositor to send an un-minimize event, though,
since it does not know if the result will be normal or maximized or
fullscreen. My main concern is now the enumeration of possible messages
is now larger than the number of states since it has to include these
"un-state" messages.
Jason Ekstrand
2013-03-25 18:31:20 UTC
Permalink
Post by Jason Ekstrand
One more thing before I go onto the technical details: Bill Spitzak.
Just because he gets on your nerves doesn't mean you should just
ignore him.
Thanks but I think my email was pretty rambling and I should not have sent
A surface can be both "minimized" and "maximized" at the same time, ie it
looks minimized but when un-minimized it then looks maximized. I thought it
might be necessary for both the client and the compositor to know this, and
thus a set of on/off flags sent as a batch would be the best way to
communicate this. A sequence of on/off command for each flag would have
annoying bugs if the two ever got out of sync.
Post by Jason Ekstrand
Thanks for your comments Bill! I have thought about this solution and
I think it would work fairly well if we were to have the client send
an explicit next state such as minimized, maximized, or normal to the
compositor.
I think you may be right that the compositor does not actually need to know
the set of states, so there is no problem with bugs making the compositor
and client disagree, which was the primary reason I thought sending the
entire set of states at once was a good idea.
This will require the compositor to send an un-minimize event, though, since
it does not know if the result will be normal or maximized or fullscreen. My
main concern is now the enumeration of possible messages is now larger than
the number of states since it has to include these "un-state" messages.
Perhaps It would help if I explained the framework in which I'm
thinking about the problem. Imagine either a window list or a
server-side decorations setup. In either case, the server needs to be
able to tell the client that the user is requesting a state change.

Now we have two orthogonal concepts to manage: the user's request to
change the state and the client telling the compositor what state to
change. These do not have to act the same in any way. The important
thing is that the compositor tells the client what was requested and
the client responds by telling the compositor what to do.

For the client instructing the compositor what to do with its windows,
I think simply setting the state it wants is probably the best option.
Otherwise, there is a lot of state tracking involved and it just
makes the protocol more complicated. When it comes to the requests
made by the user, I think they need both state and un-state forms. If
we want to keep the client in control, the compositor should never
simply tell it what state to enter. Also, the client is liable to
change the state of any of its windows without direct interaction from
the user.

There is one other little issue: what options do you present to the
user at any given time? However, I think we can get around this issue
without too much trouble.
--Jason Ekstrand

Scott Moreau
2013-03-08 04:47:10 UTC
Permalink
This patch introduces a new surface_data_manager interface that allows the
compositor to send surface data to the shell client, using the accompanying
surface_data object interface. This allows the shell client to receive
information about surfaces to build a window list, for example.
---
protocol/desktop-shell.xml | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)

diff --git a/protocol/desktop-shell.xml b/protocol/desktop-shell.xml
index d48c3dd..2fed660 100644
--- a/protocol/desktop-shell.xml
+++ b/protocol/desktop-shell.xml
@@ -82,6 +82,48 @@
</enum>
</interface>

+ <interface name="surface_data" version="1">
+ <description summary="the surface data offer object">
+ The shell can use this interface to receive surface information or make
+ requests for this surface.
+ </description>
+ <request name="destroy" type="destructor">
+ <description summary="destroy surface request">
+ The shell must send this request in response to a gone event so the
+ compositor can destroy the object properly.
+ </description>
+ </request>
+ <event name="output_mask">
+ <description summary="send the surface object output_mask to the shell"/>
+ <arg name="output_mask" type="uint"/>
+ </event>
+ <event name="title">
+ <description summary="send the surface object title to the shell"/>
+ <arg name="title" type="string"/>
+ </event>
+ <event name="gone">
+ <description summary="destroy surface notification">
+ The compositor should send this event to notify the shell that a
+ surface has been destroyed. The client must respond with a destroy
+ request.
+ </description>
+ </event>
+ </interface>
+
+ <interface name="surface_data_manager" version="1">
+ <description summary="send surface object to shell">
+ The compositor can offer surface data to a shell. The client can use
+ this interface as a way to receive special surface_data objects.
+ </description>
+ <event name="surface_object">
+ <description summary="surface object">
+ Surface object sent to a shell. This object is intended to allow the
+ shell to initiate a surface_data object interface.
+ </description>
+ <arg name="id" type="new_id" interface="surface_data"/>
+ </event>
+ </interface>
+
<interface name="screensaver" version="1">
<description summary="interface for implementing screensavers">
Only one client can bind this interface at a time.
--
1.7.10.4
Scott Moreau
2013-03-08 04:47:11 UTC
Permalink
This patch uses the special surface_data interface to send information about
the surface to the shell. The shell then uses this information to render a
window list in the panel.
---
clients/desktop-shell.c | 479 +++++++++++++++++++++++++++++++++++++++++++++--
data/Makefile.am | 1 +
data/list_item_icon.png | Bin 0 -> 176 bytes
src/compositor.c | 3 +
src/compositor.h | 1 +
src/shell.c | 201 ++++++++++++++++++++
6 files changed, 671 insertions(+), 14 deletions(-)
create mode 100644 data/list_item_icon.png

diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
index 41e7daa..2b5f7c8 100644
--- a/clients/desktop-shell.c
+++ b/clients/desktop-shell.c
@@ -44,6 +44,8 @@

#include "desktop-shell-client-protocol.h"

+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
extern char **environ; /* defined by libc */

struct desktop {
@@ -51,31 +53,55 @@ struct desktop {
struct desktop_shell *shell;
struct unlock_dialog *unlock_dialog;
struct task unlock_task;
+ struct wl_list surfaces;
struct wl_list outputs;
+ uint32_t output_count;

struct window *grab_window;
struct widget *grab_widget;

enum cursor_type grab_cursor;
+
+ struct surface_data_manager *surface_data_manager;
};

struct surface {
+ struct surface_data *surface_data;
+ struct desktop *desktop;
+ uint32_t output_mask;
+ char *title;
+
+ /* One window list item per panel of the surface's output_mask */
+ struct wl_list item_list;
+
+ struct wl_list link;
+};
+
+struct resize {
void (*configure)(void *data,
struct desktop_shell *desktop_shell,
uint32_t edges, struct window *window,
int32_t width, int32_t height);
};

+struct rgba {
+ float r, g, b, a;
+};
+
struct panel {
- struct surface base;
+ struct resize base;
struct window *window;
struct widget *widget;
struct wl_list launcher_list;
+ struct wl_list window_list;
+ struct rectangle window_list_rect;
+ uint32_t surface_count;
+ struct rgba focused_item;
struct panel_clock *clock;
};

struct background {
- struct surface base;
+ struct resize base;
struct window *window;
struct widget *widget;
};
@@ -83,11 +109,22 @@ struct background {
struct output {
struct wl_output *output;
struct wl_list link;
+ uint32_t id;

struct panel *panel;
struct background *background;
};

+struct list_item {
+ struct surface *surface;
+ struct widget *widget;
+ struct panel *panel;
+ cairo_surface_t *icon;
+ int focused, highlight;
+ struct wl_list link;
+ struct wl_list surface_link;
+};
+
struct panel_launcher {
struct widget *widget;
struct panel *panel;
@@ -249,6 +286,15 @@ set_hex_color(cairo_t *cr, uint32_t color)
}

static void
+get_hex_color_rgba(uint32_t color, float *r, float *g, float *b, float *a)
+{
+ *r = ((color >> 16) & 0xff) / 255.0;
+ *g = ((color >> 8) & 0xff) / 255.0;
+ *b = ((color >> 0) & 0xff) / 255.0;
+ *a = ((color >> 24) & 0xff) / 255.0;
+}
+
+static void
panel_redraw_handler(struct widget *widget, void *data)
{
cairo_surface_t *surface;
@@ -421,15 +467,46 @@ panel_button_handler(struct widget *widget,
}

static void
+panel_window_list_schedule_redraw(struct panel *panel)
+{
+ struct list_item *item;
+ int item_width, padding, x, w;
+
+ /* If there are no window list items, redraw the panel to clear it */
+ if (wl_list_empty(&panel->window_list)) {
+ widget_schedule_redraw(panel->widget);
+ return;
+ }
+
+ item_width = (panel->window_list_rect.width / panel->surface_count);
+ padding = 2;
+
+ x = panel->window_list_rect.x + padding;
+ w = MIN(item_width - padding, 200);
+
+ wl_list_for_each(item, &panel->window_list, link) {
+ widget_set_allocation(item->widget, x, 4, w, 24);
+
+ x += w + padding;
+ widget_schedule_redraw(item->widget);
+ }
+}
+
+static void
panel_resize_handler(struct widget *widget,
int32_t width, int32_t height, void *data)
{
struct panel_launcher *launcher;
+ struct rectangle launcher_rect;
+ struct rectangle clock_rect;
struct panel *panel = data;
int x, y, w, h;
-
+
x = 10;
y = 16;
+
+ launcher_rect.x = x;
+
wl_list_for_each(launcher, &panel->launcher_list, link) {
w = cairo_image_surface_get_width(launcher->icon);
h = cairo_image_surface_get_height(launcher->icon);
@@ -437,12 +514,25 @@ panel_resize_handler(struct widget *widget,
x, y - h / 2, w + 1, h + 1);
x += w + 10;
}
- h=20;
+
+ launcher_rect.width = x - launcher_rect.x;
+
w=170;
+ h=20;

if (panel->clock)
widget_set_allocation(panel->clock->widget,
- width - w - 8, y - h / 2, w + 1, h + 1);
+ width - w - 8, 4, w, 24);
+
+ widget_get_allocation(panel->clock->widget, &clock_rect);
+
+ panel->window_list_rect.x = launcher_rect.x + launcher_rect.width;
+ panel->window_list_rect.y = 2;
+ panel->window_list_rect.width = width -
+ panel->window_list_rect.x -
+ (clock_rect.width + 20);
+ panel->window_list_rect.height = 28;
+ panel_window_list_schedule_redraw(panel);
}

static void
@@ -451,8 +541,8 @@ panel_configure(void *data,
uint32_t edges, struct window *window,
int32_t width, int32_t height)
{
- struct surface *surface = window_get_user_data(window);
- struct panel *panel = container_of(surface, struct panel, base);
+ struct resize *resize = window_get_user_data(window);
+ struct panel *panel = container_of(resize, struct panel, base);

window_schedule_resize(panel->window, width, 32);
}
@@ -490,6 +580,22 @@ panel_destroy(struct panel *panel)
free(panel);
}

+static void
+panel_set_list_item_focus_color(struct panel *panel)
+{
+ float r, g, b, a;
+
+ /* Consider panel color when choosing item highlight color */
+ get_hex_color_rgba(key_panel_color, &r, &b, &g, &a);
+ r += 0.2;
+ g += 0.2;
+ b += 0.2;
+ panel->focused_item.r = r > 1.0 ? 0.6 : r;
+ panel->focused_item.g = g > 1.0 ? 0.6 : g;
+ panel->focused_item.b = b > 1.0 ? 0.6 : b;
+ panel->focused_item.a = 0.75;
+}
+
static struct panel *
panel_create(struct display *display)
{
@@ -502,6 +608,7 @@ panel_create(struct display *display)
panel->window = window_create_custom(display);
panel->widget = window_add_widget(panel->window, panel);
wl_list_init(&panel->launcher_list);
+ wl_list_init(&panel->window_list);

window_set_title(panel->window, "panel");
window_set_user_data(panel->window, panel);
@@ -509,7 +616,9 @@ panel_create(struct display *display)
widget_set_redraw_handler(panel->widget, panel_redraw_handler);
widget_set_resize_handler(panel->widget, panel_resize_handler);
widget_set_button_handler(panel->widget, panel_button_handler);
-
+
+ panel->surface_count = 0;
+ panel_set_list_item_focus_color(panel);
panel_add_clock(panel);

return panel;
@@ -518,18 +627,17 @@ panel_create(struct display *display)
static cairo_surface_t *
load_icon_or_fallback(const char *icon)
{
- cairo_surface_t *surface = cairo_image_surface_create_from_png(icon);
+ cairo_surface_t *surface = load_cairo_surface(icon);
cairo_t *cr;

- if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS)
+ if (surface)
return surface;

cairo_surface_destroy(surface);
fprintf(stderr, "ERROR loading icon from file '%s'\n", icon);

/* draw fallback icon */
- surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
- 20, 20);
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 20, 20);
cr = cairo_create(surface);

cairo_set_source_rgba(cr, 0.8, 0.8, 0.8, 1);
@@ -862,9 +970,9 @@ desktop_shell_configure(void *data,
int32_t width, int32_t height)
{
struct window *window = wl_surface_get_user_data(surface);
- struct surface *s = window_get_user_data(window);
+ struct resize *r = window_get_user_data(window);

- s->configure(data, desktop_shell, edges, window, width, height);
+ r->configure(data, desktop_shell, edges, window, width, height);
}

static void
@@ -946,6 +1054,328 @@ background_destroy(struct background *background)
free(background);
}

+static void
+panel_list_item_redraw_handler(struct widget *widget, void *data)
+{
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ struct list_item *item = data;
+ struct rectangle rect;
+ cairo_text_extents_t extents;
+ cairo_font_extents_t font_extents;
+ int icon_width, icon_height;
+ unsigned int dots;
+ double padding;
+ char title[128];
+
+ widget_get_allocation(widget, &rect);
+ if (rect.width == 0)
+ return;
+
+ surface = window_get_surface(item->panel->window);
+ cr = cairo_create(surface);
+
+ if (item->focused) {
+ cairo_set_source_rgba(cr, item->panel->focused_item.r,
+ item->panel->focused_item.g,
+ item->panel->focused_item.b,
+ item->panel->focused_item.a);
+ cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
+ cairo_fill(cr);
+ }
+
+ icon_width = cairo_image_surface_get_width(item->icon);
+ icon_height = cairo_image_surface_get_height(item->icon);
+ padding = (rect.height / 2.f) - (icon_height / 2.f);
+ if (rect.width > icon_width * 2) {
+ cairo_set_source_surface(cr, item->icon,
+ rect.x + padding,
+ rect.y + padding);
+ cairo_paint(cr);
+ } else {
+ icon_width = 0;
+ icon_height = 0;
+ padding = 1;
+ }
+
+ strcpy(title, item->surface->title);
+ cairo_select_font_face(cr, "sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 14);
+ cairo_text_extents(cr, title, &extents);
+
+ /* If the string is too long, clip text to button width */
+ while (extents.width > (rect.width - (icon_width + padding * 3))) {
+ title[strlen(title) - 1] = '\0';
+ cairo_text_extents(cr, title, &extents);
+ if (extents.width <= 0) {
+ title[0] = '\0';
+ break;
+ }
+ }
+
+ /* If the text is clipped, add an ellipsis */
+ dots = 3;
+ if (strlen(title) < dots)
+ dots = strlen(title) + 1;
+ if (strlen(title) != strlen(item->surface->title))
+ while (dots-- > 0)
+ title[strlen(title) - dots] = '.';
+
+ cairo_font_extents (cr, &font_extents);
+ cairo_move_to(cr, rect.x + icon_width + padding * 3 + 1,
+ rect.y + 3 * (rect.height >> 2) + 1);
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_show_text(cr, title);
+ cairo_move_to(cr, rect.x + icon_width + padding * 3,
+ rect.y + 3 * (rect.height >> 2));
+ if (item->highlight)
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ else
+ cairo_set_source_rgb(cr, 0.85, 0.85, 0.85);
+ cairo_show_text(cr, title);
+ cairo_destroy(cr);
+}
+
+static int
+panel_list_item_motion_handler(struct widget *widget, struct input *input,
+ uint32_t time, float x, float y, void *data)
+{
+ struct list_item *item = data;
+
+ widget_set_tooltip(widget, basename((char *)item->surface->title), x, y);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static int
+panel_list_item_enter_handler(struct widget *widget, struct input *input,
+ float x, float y, void *data)
+{
+ struct list_item *item = data;
+
+ item->highlight = 1;
+ widget_schedule_redraw(widget);
+
+ return CURSOR_LEFT_PTR;
+}
+
+static void
+panel_list_item_leave_handler(struct widget *widget,
+ struct input *input, void *data)
+{
+ struct list_item *item = data;
+
+ item->highlight = 0;
+ widget_destroy_tooltip(widget);
+ widget_schedule_redraw(widget);
+}
+
+static void
+panel_list_item_button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state, void *data)
+{
+ widget_schedule_redraw(widget);
+ /* TODO: Toggle minimize */
+}
+
+static struct list_item *
+panel_list_item_add(struct panel *panel, const char *icon, const char *text)
+{
+ struct list_item *item;
+ item = malloc(sizeof *item);
+ memset(item, 0, sizeof *item);
+
+ item->icon = load_icon_or_fallback(icon);
+
+ item->panel = panel;
+ wl_list_insert(panel->window_list.prev, &item->link);
+ panel->surface_count++;
+
+ item->widget = widget_add_widget(panel->widget, item);
+ widget_set_enter_handler(item->widget, panel_list_item_enter_handler);
+ widget_set_leave_handler(item->widget, panel_list_item_leave_handler);
+ widget_set_button_handler(item->widget, panel_list_item_button_handler);
+ widget_set_redraw_handler(item->widget, panel_list_item_redraw_handler);
+ widget_set_motion_handler(item->widget, panel_list_item_motion_handler);
+
+ return item;
+}
+
+static void
+panel_list_item_remove(struct list_item *item)
+{
+ item->panel->surface_count--;
+ wl_list_remove(&item->link);
+ wl_list_remove(&item->surface_link);
+ widget_destroy(item->widget);
+ panel_window_list_schedule_redraw(item->panel);
+ cairo_surface_destroy(item->icon);
+ free(item);
+}
+
+static int
+panel_list_item_exists(struct panel *panel, struct surface *surface)
+{
+ struct list_item *p_item, *s_item;
+
+ wl_list_for_each(p_item, &panel->window_list, link) {
+ wl_list_for_each(s_item, &surface->item_list, surface_link) {
+ if (p_item == s_item)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+output_update_window_list(struct output *output, struct surface *surface)
+{
+ struct list_item *item, *next;
+ struct panel *panel;
+
+ panel = output->panel;
+
+ /* Make a list item for each panel of the surfaces output mask */
+ if ((1 << output->id) & surface->output_mask) {
+ if (!panel_list_item_exists(panel, surface)) {
+ item = panel_list_item_add(panel,
+ DATADIR "/weston/list_item_icon.png",
+ surface->title);
+ wl_list_insert(surface->item_list.prev,
+ &item->surface_link);
+ item->surface = surface;
+ }
+ } else {
+ /* Remove item from panel if surface
+ * is no longer on the output */
+ wl_list_for_each_safe(item, next, &surface->item_list,
+ surface_link) {
+ if (item->panel == panel)
+ panel_list_item_remove(item);
+ }
+ }
+
+ panel_window_list_schedule_redraw(panel);
+}
+
+static void
+desktop_destroy_surface(struct surface *surface)
+{
+ struct list_item *item, *next;
+
+ wl_list_for_each_safe(item, next, &surface->item_list, surface_link)
+ panel_list_item_remove(item);
+
+ wl_list_remove(&surface->link);
+ free(surface->title);
+ free(surface);
+}
+
+static void
+desktop_update_list_items(struct desktop *desktop, struct surface *surface)
+{
+ struct output *output;
+
+ wl_list_for_each(output, &desktop->outputs, link)
+ output_update_window_list(output, surface);
+}
+
+static void
+surface_data_set_output_mask(void *data,
+ struct surface_data *surface_data,
+ uint32_t output_mask)
+{
+ struct desktop *desktop;
+ struct surface *surface = data;
+
+ desktop = surface->desktop;
+
+ surface->output_mask = output_mask;
+
+ desktop_update_list_items(desktop, surface);
+}
+
+static void
+surface_data_set_title(void *data,
+ struct surface_data *surface_data,
+ const char *title)
+{
+ struct desktop *desktop;
+ struct surface *surface = data;
+
+ desktop = surface->desktop;
+
+ if (surface->title)
+ free(surface->title);
+ surface->title = strdup(title);
+
+ desktop_update_list_items(desktop, surface);
+}
+
+static void
+surface_data_destroy_handler(void *data, struct surface_data *surface_data)
+{
+ struct list_item *item, *next;
+ struct desktop *desktop;
+ struct surface *surface = data;
+ struct output *output;
+ struct panel *panel;
+
+ desktop = surface->desktop;
+
+ surface_data_destroy(surface_data);
+
+ wl_list_for_each(output, &desktop->outputs, link) {
+ panel = output->panel;
+ wl_list_for_each_safe(item, next, &panel->window_list, link) {
+ if (surface_data == item->surface->surface_data) {
+ desktop_destroy_surface(item->surface);
+ return;
+ }
+ }
+ }
+}
+
+static const struct surface_data_listener surface_data_listener = {
+ surface_data_set_output_mask,
+ surface_data_set_title,
+ surface_data_destroy_handler
+};
+
+static void
+surface_data_receive_surface_object(void *data,
+ struct surface_data_manager *manager,
+ struct surface_data *surface_data)
+{
+ struct desktop *desktop = data;
+ struct surface *surface;
+
+ surface = calloc(1, sizeof *surface);
+
+ if (!surface) {
+ fprintf(stderr, "ERROR: Failed to allocate memory!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ surface->surface_data = surface_data;
+ surface->desktop = desktop;
+ surface->title = strdup("unknown");
+ surface->output_mask = 1;
+ wl_list_init(&surface->item_list);
+ wl_list_insert(&desktop->surfaces, &surface->link);
+ surface_data_add_listener(surface_data,
+ &surface_data_listener, surface);
+}
+
+static const struct surface_data_manager_listener surface_data_manager_listener = {
+ surface_data_receive_surface_object
+};
+
static struct background *
background_create(struct desktop *desktop)
{
@@ -1022,6 +1452,15 @@ desktop_destroy_outputs(struct desktop *desktop)
}

static void
+desktop_destroy_surfaces(struct desktop *desktop)
+{
+ struct surface *surface, *next;
+
+ wl_list_for_each_safe(surface, next, &desktop->surfaces, link)
+ desktop_destroy_surface(surface);
+}
+
+static void
create_output(struct desktop *desktop, uint32_t id)
{
struct output *output;
@@ -1033,6 +1472,8 @@ create_output(struct desktop *desktop, uint32_t id)
output->output =
display_bind(desktop->display, id, &wl_output_interface, 1);

+ output->id = desktop->output_count++;
+
wl_list_insert(&desktop->outputs, &output->link);
}

@@ -1048,6 +1489,12 @@ global_handler(struct display *display, uint32_t id,
desktop_shell_add_listener(desktop->shell, &listener, desktop);
} else if (!strcmp(interface, "wl_output")) {
create_output(desktop, id);
+ } else if (!strcmp(interface, "surface_data_manager")) {
+ desktop->surface_data_manager =
+ display_bind(display, id,
+ &surface_data_manager_interface, 1);
+ surface_data_manager_add_listener(desktop->surface_data_manager,
+ &surface_data_manager_listener, desktop);
}
}

@@ -1100,6 +1547,9 @@ int main(int argc, char *argv[])
return -1;
}

+ wl_list_init(&desktop.surfaces);
+ desktop.output_count = 0;
+
display_set_user_data(desktop.display, &desktop);
display_set_global_handler(desktop.display, global_handler);

@@ -1133,6 +1583,7 @@ int main(int argc, char *argv[])

/* Cleanup */
grab_surface_destroy(&desktop);
+ desktop_destroy_surfaces(&desktop);
desktop_destroy_outputs(&desktop);
if (desktop.unlock_dialog)
unlock_dialog_destroy(desktop.unlock_dialog);
diff --git a/data/Makefile.am b/data/Makefile.am
index a7cc944..293d2bc 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -7,6 +7,7 @@ dist_westondata_DATA = \
terminal.png \
border.png \
icon_window.png \
+ list_item_icon.png \
sign_close.png \
sign_maximize.png \
sign_minimize.png
diff --git a/data/list_item_icon.png b/data/list_item_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac987e997313910ec3e02a11be8ccda0dfa581c7
GIT binary patch
literal 176
zcmeAS at N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b
z3=G`DAk4 at xYmNj^kiEpy*OmPaH$RJ!-UgFp7lA^yo-U3d7N_4%cI0AE;5i(0^Z))B
z|EBQpgWZ)55eu3H82wmS6TZt#lE`$@S at Cy{aM-usE&4llHh-IQIlXHpO8~cw3TFo|
P&>#j+S3j3^P6<r_g#|R0

literal 0
HcmV?d00001

diff --git a/src/compositor.c b/src/compositor.c
index a2860fd..c6637c3 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -509,6 +509,8 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
if (1 << output->id & left)
wl_surface_send_leave(&es->surface.resource, resource);
}
+
+ wl_signal_emit(&es->compositor->output_mask_update_signal, es);
}

static void
@@ -3049,6 +3051,7 @@ weston_compositor_init(struct weston_compositor *ec,
wl_signal_init(&ec->wake_signal);
wl_signal_init(&ec->show_input_panel_signal);
wl_signal_init(&ec->hide_input_panel_signal);
+ wl_signal_init(&ec->output_mask_update_signal);
wl_signal_init(&ec->seat_created_signal);
ec->launcher_sock = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");

diff --git a/src/compositor.h b/src/compositor.h
index 4a0c1e3..36ec337 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -301,6 +301,7 @@ struct weston_compositor {

struct wl_signal show_input_panel_signal;
struct wl_signal hide_input_panel_signal;
+ struct wl_signal output_mask_update_signal;

struct wl_signal seat_created_signal;

diff --git a/src/shell.c b/src/shell.c
index 3da5321..5f09a45 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -88,6 +88,7 @@ struct desktop_shell {
struct wl_listener destroy_listener;
struct wl_listener show_input_panel_listener;
struct wl_listener hide_input_panel_listener;
+ struct wl_listener output_mask_update_listener;

struct weston_layer fullscreen_layer;
struct weston_layer panel_layer;
@@ -107,6 +108,8 @@ struct desktop_shell {
uint32_t deathstamp;
} child;

+ struct wl_resource *surface_data_manager;
+
bool locked;
bool showing_input_panels;
bool prepare_event_sent;
@@ -217,6 +220,7 @@ struct shell_surface {
struct wl_list link;

const struct weston_shell_client *client;
+ struct wl_resource *surface_data;
};

struct shell_grab {
@@ -1415,6 +1419,110 @@ shell_surface_pong(struct wl_client *client, struct wl_resource *resource,
}

static void
+surface_data_object_destroy(struct wl_resource *resource)
+{
+ struct shell_surface *shsurf = resource->data;
+
+ free(resource);
+
+ if (!shsurf)
+ return;
+
+ shsurf->surface_data = NULL;
+}
+
+static void
+surface_data_destroy_handler(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct surface_data_interface
+ surface_data_implementation = {
+ surface_data_destroy_handler
+};
+
+static int
+create_surface_data(struct desktop_shell *shell, struct shell_surface *shsurf)
+{
+ struct wl_resource *surface_data;
+
+ if (shsurf->surface_data)
+ return -1;
+
+ surface_data = malloc(sizeof *surface_data);
+ if (surface_data == NULL)
+ return -1;
+
+ surface_data->data = shsurf;
+ surface_data->object.id = 0;
+ surface_data->object.interface = &surface_data_interface;
+ surface_data->destroy = surface_data_object_destroy;
+ surface_data->object.implementation =
+ (void (**)(void)) &surface_data_implementation;
+ wl_signal_init(&surface_data->destroy_signal);
+
+ wl_client_add_resource(shell->surface_data_manager->client, surface_data);
+
+ shsurf->surface_data = surface_data;
+
+ surface_data_manager_send_surface_object(shell->surface_data_manager,
+ shsurf->surface_data);
+
+ return 0;
+}
+
+static bool
+surface_is_window_list_candidate(struct weston_surface *surface)
+{
+ struct desktop_shell *shell;
+ struct shell_surface *shsurf;
+
+ shsurf = get_shell_surface(surface);
+ if (!shsurf)
+ return false;
+
+ shell = shsurf->shell;
+
+ if (!shell->surface_data_manager)
+ return false;
+
+ switch (shsurf->type) {
+ default:
+ case SHELL_SURFACE_TRANSIENT:
+ case SHELL_SURFACE_POPUP:
+ case SHELL_SURFACE_NONE:
+ return false;
+ case SHELL_SURFACE_FULLSCREEN:
+ case SHELL_SURFACE_MAXIMIZED:
+ case SHELL_SURFACE_TOPLEVEL:
+ return true;
+ }
+}
+
+static void
+send_surface_data_output_mask(struct weston_surface *surface)
+{
+ struct shell_surface *shsurf = get_shell_surface(surface);
+
+ if (shsurf && shsurf->surface_data)
+ surface_data_send_output_mask(shsurf->surface_data,
+ surface->output_mask);
+}
+
+static void
+send_surface_data_title(struct weston_surface *surface)
+{
+ struct shell_surface *shsurf = get_shell_surface(surface);
+
+ if (shsurf && shsurf->surface_data)
+ surface_data_send_title(shsurf->surface_data,
+ shsurf->title == NULL ?
+ "Surface" : shsurf->title);
+}
+
+static void
shell_surface_set_title(struct wl_client *client,
struct wl_resource *resource, const char *title)
{
@@ -1422,6 +1530,7 @@ shell_surface_set_title(struct wl_client *client,

free(shsurf->title);
shsurf->title = strdup(title);
+ send_surface_data_title(shsurf->surface);
}

static void
@@ -1538,6 +1647,10 @@ set_surface_type(struct shell_surface *shsurf)
default:
break;
}
+
+ if (surface_is_window_list_candidate(shsurf->surface))
+ create_surface_data(shsurf->shell, shsurf);
+ send_surface_data_title(surface);
}

static void
@@ -1984,6 +2097,8 @@ static const struct wl_shell_surface_interface shell_surface_implementation = {
static void
destroy_shell_surface(struct shell_surface *shsurf)
{
+ if (shsurf->surface_data)
+ surface_data_send_gone(shsurf->surface_data);
if (shsurf->popup.grab.pointer)
wl_pointer_end_grab(shsurf->popup.grab.pointer);

@@ -2390,6 +2505,37 @@ static const struct desktop_shell_interface desktop_shell_implementation = {
desktop_shell_set_grab_surface
};

+static void
+surface_data_create_all_objects(struct desktop_shell *shell)
+{
+ struct weston_surface *surface;
+ struct shell_surface *shsurf;
+ struct workspace *ws;
+
+ ws = get_current_workspace(shell);
+
+ wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+ if (surface_is_window_list_candidate(surface)) {
+ shsurf = get_shell_surface(surface);
+ create_surface_data(shell, shsurf);
+ }
+ }
+}
+
+static void
+surface_data_send_all_info(struct desktop_shell *shell)
+{
+ struct weston_surface *surface;
+ struct workspace *ws;
+
+ ws = get_current_workspace(shell);
+
+ wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+ send_surface_data_output_mask(surface);
+ send_surface_data_title(surface);
+ }
+}
+
static enum shell_surface_type
get_shell_surface_type(struct weston_surface *surface)
{
@@ -2946,6 +3092,22 @@ show_input_panels(struct wl_listener *listener, void *data)
}

static void
+output_mask_update(struct wl_listener *listener, void *data)
+{
+ struct weston_surface *surface = data;
+ struct shell_surface *shsurf;
+
+ if (!surface)
+ return;
+
+ shsurf = get_shell_surface(surface);
+
+ if (shsurf && shsurf->surface_data)
+ surface_data_send_output_mask(shsurf->surface_data,
+ surface->output_mask);
+}
+
+static void
hide_input_panels(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell =
@@ -3304,6 +3466,38 @@ bind_desktop_shell(struct wl_client *client,
}

static void
+unbind_surface_data_manager(struct wl_resource *resource)
+{
+ struct desktop_shell *shell = resource->data;
+
+ shell->surface_data_manager = NULL;
+ free(resource);
+}
+
+static void
+bind_surface_data_manager(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct desktop_shell *shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_client_add_object(client, &surface_data_manager_interface,
+ NULL, id, shell);
+
+ if (client == shell->child.client) {
+ resource->destroy = unbind_surface_data_manager;
+ shell->surface_data_manager = resource;
+ surface_data_create_all_objects(shell);
+ surface_data_send_all_info(shell);
+ return;
+ }
+
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "permission to bind desktop_shell denied");
+ wl_resource_destroy(resource);
+}
+
+static void
screensaver_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height)
{
struct desktop_shell *shell = surface->private;
@@ -3978,6 +4172,7 @@ shell_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&shell->wake_listener.link);
wl_list_remove(&shell->show_input_panel_listener.link);
wl_list_remove(&shell->hide_input_panel_listener.link);
+ wl_list_remove(&shell->output_mask_update_listener.link);

wl_array_for_each(ws, &shell->workspaces.array)
workspace_destroy(*ws);
@@ -4087,6 +4282,8 @@ module_init(struct weston_compositor *ec,
wl_signal_add(&ec->show_input_panel_signal, &shell->show_input_panel_listener);
shell->hide_input_panel_listener.notify = hide_input_panels;
wl_signal_add(&ec->hide_input_panel_signal, &shell->hide_input_panel_listener);
+ shell->output_mask_update_listener.notify = output_mask_update;
+ wl_signal_add(&ec->output_mask_update_signal, &shell->output_mask_update_listener);
ec->ping_handler = ping_handler;
ec->shell_interface.shell = shell;
ec->shell_interface.create_shell_surface = create_shell_surface;
@@ -4144,6 +4341,10 @@ module_init(struct weston_compositor *ec,
if (wl_display_add_global(ec->wl_display, &workspace_manager_interface,
shell, bind_workspace_manager) == NULL)
return -1;
+ if (wl_display_add_global(ec->wl_display, &surface_data_manager_interface,
+ shell, bind_surface_data_manager) == NULL)
+ return -1;
+

shell->child.deathstamp = weston_compositor_get_time();
--
1.7.10.4
Scott Moreau
2013-03-08 04:47:12 UTC
Permalink
---
protocol/desktop-shell.xml | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/protocol/desktop-shell.xml b/protocol/desktop-shell.xml
index 2fed660..51219d2 100644
--- a/protocol/desktop-shell.xml
+++ b/protocol/desktop-shell.xml
@@ -87,6 +87,18 @@
The shell can use this interface to receive surface information or make
requests for this surface.
</description>
+ <request name="minimize">
+ <description summary="ask the compositor to minimize the surface"/>
+ </request>
+ <request name="unminimize">
+ <description summary="ask the compositor to unminimize the surface"/>
+ </request>
+ <request name="focus">
+ <description summary="ask the compositor to focus the surface"/>
+ </request>
+ <request name="close">
+ <description summary="ask the compositor to close the surface"/>
+ </request>
<request name="destroy" type="destructor">
<description summary="destroy surface request">
The shell must send this request in response to a gone event so the
@@ -101,6 +113,14 @@
<description summary="send the surface object title to the shell"/>
<arg name="title" type="string"/>
</event>
+ <event name="minimized">
+ <description summary="send the surface object minimize state to the shell"/>
+ <arg name="minimized" type="int"/>
+ </event>
+ <event name="focused">
+ <description summary="send the surface object focus state to the shell"/>
+ <arg name="focused" type="int"/>
+ </event>
<event name="gone">
<description summary="destroy surface notification">
The compositor should send this event to notify the shell that a
--
1.7.10.4
Scott Moreau
2013-03-08 04:47:13 UTC
Permalink
Right-click dropdown menu for window list items. This patch introduces a simple
minimize feature. The surface is removed from the layer list on minimize and
re-added on unminimize. The close item sends the client a SIGTERM signal.
---
clients/desktop-shell.c | 154 ++++++++++++++++++++++++++++++++--
src/shell.c | 214 +++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 357 insertions(+), 11 deletions(-)

diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
index 2b5f7c8..582a19f 100644
--- a/clients/desktop-shell.c
+++ b/clients/desktop-shell.c
@@ -70,6 +70,7 @@ struct surface {
struct desktop *desktop;
uint32_t output_mask;
char *title;
+ int minimized, focused;

/* One window list item per panel of the surface's output_mask */
struct wl_list item_list;
@@ -193,13 +194,13 @@ sigchild_handler(int s)
}

static void
-menu_func(struct window *window, int index, void *data)
+panel_menu_func(struct window *window, int index, void *data)
{
printf("Selected index %d from a panel menu.\n", index);
}

static void
-show_menu(struct panel *panel, struct input *input, uint32_t time)
+panel_show_menu(struct panel *panel, struct input *input, uint32_t time)
{
int32_t x, y;
static const char *entries[] = {
@@ -209,7 +210,7 @@ show_menu(struct panel *panel, struct input *input, uint32_t time)
input_get_position(input, &x, &y);
window_show_menu(window_get_display(panel->window),
input, time, panel->window,
- x - 10, y - 10, menu_func, entries, 4);
+ x - 10, y - 10, panel_menu_func, entries, 4);
}

static void
@@ -463,7 +464,7 @@ panel_button_handler(struct widget *widget,
struct panel *panel = data;

if (button == BTN_RIGHT && state == WL_POINTER_BUTTON_STATE_PRESSED)
- show_menu(panel, input, time);
+ panel_show_menu(panel, input, time);
}

static void
@@ -1075,7 +1076,7 @@ panel_list_item_redraw_handler(struct widget *widget, void *data)
surface = window_get_surface(item->panel->window);
cr = cairo_create(surface);

- if (item->focused) {
+ if (item->focused || item->surface->focused) {
cairo_set_source_rgba(cr, item->panel->focused_item.r,
item->panel->focused_item.g,
item->panel->focused_item.b,
@@ -1156,6 +1157,7 @@ panel_list_item_enter_handler(struct widget *widget, struct input *input,
struct list_item *item = data;

item->highlight = 1;
+ item->focused = 1;
widget_schedule_redraw(widget);

return CURSOR_LEFT_PTR;
@@ -1168,18 +1170,116 @@ panel_list_item_leave_handler(struct widget *widget,
struct list_item *item = data;

item->highlight = 0;
+ item->focused = 0;
widget_destroy_tooltip(widget);
widget_schedule_redraw(widget);
}

static void
+desktop_update_list_items(struct desktop *desktop, struct surface *surface);
+
+static void
+list_item_menu_handle_button(struct list_item *item, int index)
+{
+ struct surface *surface = item->surface;
+
+ switch (index) {
+ case 0: /* (Un)Minimize */
+ if (surface->minimized) {
+ surface_data_unminimize(surface->surface_data);
+ surface->minimized = 0;
+ }
+ else {
+ surface_data_minimize(surface->surface_data);
+ surface->minimized = 1;
+ }
+ break;
+ case 1: /* Close */
+ surface_data_close(surface->surface_data);
+ break;
+ default:
+ item->highlight = 0;
+ break;
+ }
+
+ item->focused = 0;
+
+ desktop_update_list_items(surface->desktop, surface);
+ widget_destroy_tooltip(item->widget);
+ widget_schedule_redraw(item->widget);
+}
+
+static void
+list_item_menu_func(struct window *window, int index, void *data)
+{
+ struct list_item *item;
+ struct panel *panel;
+
+ panel = data;
+
+ wl_list_for_each(item, &panel->window_list, link)
+ if (item->focused) {
+ list_item_menu_handle_button(item, index);
+ return;
+ }
+}
+
+#define MENU_ENTRIES 2
+
+static void
+list_item_show_menu(struct list_item *item, struct input *input, uint32_t time)
+{
+ struct panel *panel;
+ int32_t x, y;
+ static const char *entries[MENU_ENTRIES];
+
+ entries[0] = item->surface->minimized ? "Unminimize" : "Minimize";
+ entries[1] = "Close";
+
+ panel = item->panel;
+ input_get_position(input, &x, &y);
+ window_show_menu(window_get_display(panel->window), input,
+ time, panel->window, x - 10, y - 10,
+ list_item_menu_func, entries, MENU_ENTRIES);
+}
+
+static void
panel_list_item_button_handler(struct widget *widget,
struct input *input, uint32_t time,
uint32_t button,
enum wl_pointer_button_state state, void *data)
{
+ struct list_item *item;
+ struct surface *surface;
+
+ item = data;
+
widget_schedule_redraw(widget);
- /* TODO: Toggle minimize */
+
+ if (button == BTN_RIGHT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ widget_destroy_tooltip(item->widget);
+ widget_schedule_redraw(item->widget);
+ list_item_show_menu(item, input, time);
+ return;
+ }
+
+ if ((button != BTN_LEFT) || (state != WL_POINTER_BUTTON_STATE_RELEASED))
+ return;
+
+ surface = item->surface;
+ if (!surface->focused && !surface->minimized) {
+ surface_data_focus(surface->surface_data);
+ surface->focused = 1;
+ return;
+ }
+ if (surface->minimized) {
+ surface_data_unminimize(surface->surface_data);
+ surface->minimized = 0;
+ }
+ else {
+ surface_data_minimize(surface->surface_data);
+ surface->minimized = 1;
+ }
}

static struct list_item *
@@ -1318,6 +1418,45 @@ surface_data_set_title(void *data,
}

static void
+surface_data_set_minimized_state(void *data,
+ struct surface_data *surface_data,
+ int minimized)
+{
+ struct desktop *desktop;
+ struct surface *surface = data;
+
+ desktop = surface->desktop;
+
+ surface->minimized = minimized;
+
+ desktop_update_list_items(desktop, surface);
+}
+
+static void
+surface_data_set_focused_state(void *data,
+ struct surface_data *surface_data,
+ int focused)
+{
+ struct desktop *desktop;
+ struct surface *surface = data, *es;
+ struct list_item *item;
+
+ desktop = surface->desktop;
+
+ wl_list_for_each(es, &desktop->surfaces, link)
+ if (es->surface_data != surface_data && focused) {
+ es->focused = 0;
+ wl_list_for_each(item, &es->item_list, surface_link)
+ if (!item->focused)
+ item->highlight = 0;
+ }
+
+ surface->focused = focused;
+
+ desktop_update_list_items(desktop, surface);
+}
+
+static void
surface_data_destroy_handler(void *data, struct surface_data *surface_data)
{
struct list_item *item, *next;
@@ -1344,6 +1483,8 @@ surface_data_destroy_handler(void *data, struct surface_data *surface_data)
static const struct surface_data_listener surface_data_listener = {
surface_data_set_output_mask,
surface_data_set_title,
+ surface_data_set_minimized_state,
+ surface_data_set_focused_state,
surface_data_destroy_handler
};

@@ -1362,6 +1503,7 @@ surface_data_receive_surface_object(void *data,
exit(EXIT_FAILURE);
}

+ surface->desktop = desktop;
surface->surface_data = surface_data;
surface->desktop = desktop;
surface->title = strdup("unknown");
diff --git a/src/shell.c b/src/shell.c
index 5f09a45..9e19ddb 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -67,6 +67,7 @@ struct workspace {
struct weston_layer layer;

struct wl_list focus_list;
+ struct wl_list minimized_list;
struct wl_listener seat_destroyed_listener;
};

@@ -178,11 +179,12 @@ struct shell_surface {
struct weston_surface *parent;
struct desktop_shell *shell;

- enum shell_surface_type type, next_type;
+ enum shell_surface_type type, next_type, saved_type;
char *title, *class;
int32_t saved_x, saved_y;
bool saved_position_valid;
bool saved_rotation_valid;
+ int minimized;
int unresponsive;

struct {
@@ -556,6 +558,7 @@ workspace_create(void)
weston_layer_init(&ws->layer, NULL);

wl_list_init(&ws->focus_list);
+ wl_list_init(&ws->minimized_list);
wl_list_init(&ws->seat_destroyed_listener.link);
ws->seat_destroyed_listener.notify = seat_destroyed;

@@ -1419,16 +1422,163 @@ shell_surface_pong(struct wl_client *client, struct wl_resource *resource,
}

static void
-surface_data_object_destroy(struct wl_resource *resource)
+send_surface_data_focused_state(struct weston_surface *surface);
+
+static void
+activate(struct desktop_shell *shell, struct weston_surface *es,
+ struct weston_seat *seat);
+
+static void
+shell_surface_focus(struct shell_surface *shsurf)
+{
+ struct desktop_shell *shell;
+ struct weston_compositor *compositor;
+ struct weston_surface *surface;
+ struct weston_seat *seat;
+
+ shell = shsurf->shell;
+ compositor = shell->compositor;
+ surface = shsurf->surface;
+
+ wl_list_for_each(seat, &surface->compositor->seat_list, link)
+ if (seat->seat.keyboard) {
+ wl_keyboard_set_focus(seat->seat.keyboard,
+ &surface->surface);
+ activate(shell, surface, seat);
+ }
+
+ weston_compositor_damage_all(compositor);
+}
+
+static void
+shell_surface_minimize(struct shell_surface *shsurf)
+{
+ struct desktop_shell *shell;
+ struct weston_compositor *compositor;
+ struct weston_surface *surface;
+ struct workspace *ws;
+ struct weston_seat *seat;
+ struct weston_surface *focus;
+
+ shell = shsurf->shell;
+ compositor = shell->compositor;
+ surface = shsurf->surface;
+ ws = get_current_workspace(shell);
+
+ wl_list_remove(&surface->layer_link);
+ wl_list_insert(ws->minimized_list.prev, &surface->layer_link);
+ shsurf->saved_type = shsurf->type;
+ shsurf->minimized = 1;
+
+ /* Focus next surface in stack */
+ wl_list_for_each(seat, &compositor->seat_list, link)
+ if (seat->seat.keyboard &&
+ seat->keyboard.keyboard.focus == &surface->surface) {
+ if (!wl_list_empty(&ws->layer.surface_list)) {
+ focus = container_of(ws->layer.surface_list.next,
+ struct weston_surface,
+ layer_link);
+ shsurf = get_shell_surface(focus);
+ if (!shsurf)
+ break;
+ shell_surface_focus(shsurf);
+ } else
+ wl_keyboard_set_focus(seat->seat.keyboard, NULL);
+ }
+
+ send_surface_data_focused_state(surface);
+ weston_compositor_damage_all(compositor);
+}
+
+static void
+surface_unminimize(struct shell_surface *shsurf, struct workspace *ws)
+{
+ struct desktop_shell *shell;
+ struct weston_compositor *compositor;
+ struct weston_surface *surface;
+
+ shell = shsurf->shell;
+ compositor = shell->compositor;
+ surface = shsurf->surface;
+
+ wl_list_remove(&surface->layer_link);
+ wl_list_insert(ws->layer.surface_list.prev, &surface->layer_link);
+ shell_surface_focus(shsurf);
+ send_surface_data_focused_state(surface);
+ shsurf->minimized = false;
+ weston_compositor_damage_all(compositor);
+}
+
+static void
+shell_surface_unminimize(struct shell_surface *shsurf)
+{
+ struct weston_surface *surface;
+ struct workspace *ws = get_current_workspace(shsurf->shell);
+
+ wl_list_for_each(surface, &ws->minimized_list, layer_link)
+ if (surface == shsurf->surface) {
+ surface_unminimize(shsurf, ws);
+ return;
+ }
+}
+
+static void
+surface_data_minimize_handler(struct wl_client *client,
+ struct wl_resource *resource)
{
struct shell_surface *shsurf = resource->data;

- free(resource);
+ shell_surface_minimize(shsurf);
+}

- if (!shsurf)
+static void
+surface_data_unminimize_handler(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct shell_surface *shsurf = resource->data;
+
+ shell_surface_unminimize(shsurf);
+}
+
+static void
+surface_data_focus_handler(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct shell_surface *shsurf = resource->data;
+
+ shell_surface_focus(shsurf);
+}
+
+static void
+surface_data_close_handler(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct shell_surface *shsurf;
+ struct wl_surface *target_surface;
+ struct wl_client *target_client;
+ struct desktop_shell *shell;
+ struct weston_compositor *compositor;
+ pid_t pid;
+
+ shsurf = resource->data;
+ target_surface = &shsurf->surface->surface;
+ shell = shsurf->shell;
+ compositor = shell->compositor;
+
+ if (!target_surface)
return;

- shsurf->surface_data = NULL;
+ wl_signal_emit(&compositor->kill_signal, target_surface);
+
+ target_client = target_surface->resource.client;
+ wl_client_get_credentials(target_client, &pid, NULL, NULL);
+
+ /* Skip clients that we launched ourselves (the credentials of
+ * the socketpair is ours) */
+ if (pid == getpid())
+ return;
+
+ kill(pid, SIGTERM);
}

static void
@@ -1440,9 +1590,26 @@ surface_data_destroy_handler(struct wl_client *client,

static const struct surface_data_interface
surface_data_implementation = {
+ surface_data_minimize_handler,
+ surface_data_unminimize_handler,
+ surface_data_focus_handler,
+ surface_data_close_handler,
surface_data_destroy_handler
};

+static void
+surface_data_object_destroy(struct wl_resource *resource)
+{
+ struct shell_surface *shsurf = resource->data;
+
+ free(resource);
+
+ if (!shsurf)
+ return;
+
+ shsurf->surface_data = NULL;
+}
+
static int
create_surface_data(struct desktop_shell *shell, struct shell_surface *shsurf)
{
@@ -1523,6 +1690,34 @@ send_surface_data_title(struct weston_surface *surface)
}

static void
+send_surface_data_minimized_state(struct weston_surface *surface)
+{
+ struct shell_surface *shsurf = get_shell_surface(surface);
+
+ if (shsurf && shsurf->surface_data)
+ surface_data_send_minimized(shsurf->surface_data,
+ shsurf->minimized ? 1 : 0);
+}
+
+static void
+send_surface_data_focused_state(struct weston_surface *surface)
+{
+ struct shell_surface *shsurf = get_shell_surface(surface);
+ struct focus_state *state;
+ struct workspace *ws;
+ bool focused = false;
+
+ if (shsurf && shsurf->surface_data) {
+ ws = get_current_workspace(shsurf->shell);
+ wl_list_for_each(state, &ws->focus_list, link)
+ if (state->keyboard_focus == shsurf->surface)
+ focused = true;
+ surface_data_send_focused(shsurf->surface_data,
+ focused);
+ }
+}
+
+static void
shell_surface_set_title(struct wl_client *client,
struct wl_resource *resource, const char *title)
{
@@ -2531,6 +2726,14 @@ surface_data_send_all_info(struct desktop_shell *shell)
ws = get_current_workspace(shell);

wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+ send_surface_data_minimized_state(surface);
+ send_surface_data_focused_state(surface);
+ send_surface_data_output_mask(surface);
+ send_surface_data_title(surface);
+ }
+ wl_list_for_each(surface, &ws->minimized_list, layer_link) {
+ send_surface_data_minimized_state(surface);
+ send_surface_data_focused_state(surface);
send_surface_data_output_mask(surface);
send_surface_data_title(surface);
}
@@ -2869,6 +3072,7 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
return;

state->keyboard_focus = es;
+ send_surface_data_focused_state(es);
wl_list_remove(&state->surface_destroy_listener.link);
wl_signal_add(&es->surface.resource.destroy_signal,
&state->surface_destroy_listener);
--
1.7.10.4
Scott Moreau
2013-03-08 04:47:14 UTC
Permalink
This allows the user to rearrange the list items by drag-and-drop. There is no
drag icon, only preliminary functionality. Eventually, this could be expanded
to use the wayland dnd protocol.
---
clients/desktop-shell.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 85 insertions(+)

diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
index 582a19f..1fa7387 100644
--- a/clients/desktop-shell.c
+++ b/clients/desktop-shell.c
@@ -122,8 +122,10 @@ struct list_item {
struct panel *panel;
cairo_surface_t *icon;
int focused, highlight;
+ int x, y;
struct wl_list link;
struct wl_list surface_link;
+ struct wl_list reorder_link;
};

struct panel_launcher {
@@ -1145,6 +1147,9 @@ panel_list_item_motion_handler(struct widget *widget, struct input *input,
{
struct list_item *item = data;

+ item->x = x;
+ item->y = y;
+
widget_set_tooltip(widget, basename((char *)item->surface->title), x, y);

return CURSOR_LEFT_PTR;
@@ -1156,6 +1161,8 @@ panel_list_item_enter_handler(struct widget *widget, struct input *input,
{
struct list_item *item = data;

+ item->x = x;
+ item->y = y;
item->highlight = 1;
item->focused = 1;
widget_schedule_redraw(widget);
@@ -1243,6 +1250,79 @@ list_item_show_menu(struct list_item *item, struct input *input, uint32_t time)
list_item_menu_func, entries, MENU_ENTRIES);
}

+static int
+rect_contains_point(struct rectangle rect, int x, int y)
+{
+ int x1, y1, x2, y2;
+
+ x1 = rect.x;
+ y1 = rect.y;
+ x2 = rect.x + rect.width;
+ y2 = rect.y + rect.height;
+
+ if (x > x1 && x < x2 && y > y1 && y < y2)
+ return 1;
+
+ return 0;
+}
+
+static int
+item_contains_point(struct list_item *item, int x, int y)
+{
+ struct rectangle item_rect;
+
+ widget_get_allocation(item->widget, &item_rect);
+
+ return rect_contains_point(item_rect, x, y);
+}
+
+static int
+list_contains_point(struct list_item *item, int x, int y)
+{
+ struct rectangle list_rect;
+
+ list_rect = item->panel->window_list_rect;
+
+ return rect_contains_point(list_rect, x, y);
+}
+
+static void
+panel_item_list_reorder(struct panel *panel,
+ struct list_item *current, struct list_item *item)
+{
+ struct rectangle current_rect, item_rect;
+
+ if (current == item)
+ return;
+
+ widget_get_allocation(current->widget, &current_rect);
+ widget_get_allocation(item->widget, &item_rect);
+
+ wl_list_remove(&current->link);
+
+ if (item_rect.x < current_rect.x)
+ wl_list_insert(item->link.prev, &current->link);
+ else
+ wl_list_insert(&item->link, &current->link);
+
+ panel_window_list_schedule_redraw(item->panel);
+}
+
+static void
+list_item_move(struct list_item *current, int x, int y)
+{
+ struct list_item *item;
+
+ wl_list_for_each(item, &current->panel->window_list, link) {
+ if (item == current)
+ continue;
+ if (item_contains_point(item, x, y)) {
+ panel_item_list_reorder(item->panel, current, item);
+ return;
+ }
+ }
+}
+
static void
panel_list_item_button_handler(struct widget *widget,
struct input *input, uint32_t time,
@@ -1267,6 +1347,11 @@ panel_list_item_button_handler(struct widget *widget,
return;

surface = item->surface;
+ if (!item_contains_point(item, item->x, item->y)) {
+ if (list_contains_point(item, item->x, item->y))
+ list_item_move(item, item->x, item->y);
+ return;
+ }
if (!surface->focused && !surface->minimized) {
surface_data_focus(surface->surface_data);
surface->focused = 1;
--
1.7.10.4
Scott Moreau
2013-03-08 04:47:15 UTC
Permalink
---
clients/simple-egl.c | 26 +++++++++++++++++++++++++-
clients/simple-shm.c | 26 +++++++++++++++++++++++++-
clients/simple-touch.c | 26 +++++++++++++++++++++++++-
clients/window.c | 26 +++++++++++++++++++++++++-
src/shell.c | 9 ++++++++-
5 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/clients/simple-egl.c b/clients/simple-egl.c
index 26ebe5c..d90ecf5 100644
--- a/clients/simple-egl.c
+++ b/clients/simple-egl.c
@@ -246,10 +246,34 @@ handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
{
}

+static void
+handle_maximize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_unmaximize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_minimize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_unminimize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
static const struct wl_shell_surface_listener shell_surface_listener = {
handle_ping,
handle_configure,
- handle_popup_done
+ handle_popup_done,
+ handle_maximize,
+ handle_unmaximize,
+ handle_minimize,
+ handle_unminimize
};

static void
diff --git a/clients/simple-shm.c b/clients/simple-shm.c
index f187b10..ded27c5 100644
--- a/clients/simple-shm.c
+++ b/clients/simple-shm.c
@@ -126,10 +126,34 @@ handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
{
}

+static void
+handle_maximize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_unmaximize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_minimize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_unminimize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
static const struct wl_shell_surface_listener shell_surface_listener = {
handle_ping,
handle_configure,
- handle_popup_done
+ handle_popup_done,
+ handle_maximize,
+ handle_unmaximize,
+ handle_minimize,
+ handle_unminimize
};

static struct window *
diff --git a/clients/simple-touch.c b/clients/simple-touch.c
index b8473f1..8ebb29b 100644
--- a/clients/simple-touch.c
+++ b/clients/simple-touch.c
@@ -233,10 +233,34 @@ handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
{
}

+static void
+handle_maximize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_unmaximize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_minimize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_unminimize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
static const struct wl_shell_surface_listener shell_surface_listener = {
handle_ping,
handle_configure,
- handle_popup_done
+ handle_popup_done,
+ handle_maximize,
+ handle_unmaximize,
+ handle_minimize,
+ handle_unminimize
};

static void
diff --git a/clients/window.c b/clients/window.c
index 249ba6f..d13a1ac 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -3313,10 +3313,34 @@ handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
menu_destroy(menu);
}

+static void
+handle_maximize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_unmaximize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_minimize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static void
+handle_unminimize(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
static const struct wl_shell_surface_listener shell_surface_listener = {
handle_ping,
handle_configure,
- handle_popup_done
+ handle_popup_done,
+ handle_maximize,
+ handle_unmaximize,
+ handle_minimize,
+ handle_unminimize
};

void
diff --git a/src/shell.c b/src/shell.c
index 9e19ddb..f3877d2 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1944,6 +1944,12 @@ shell_surface_set_maximized(struct wl_client *client,
}

static void
+shell_surface_set_minimized(struct wl_client *client,
+ struct wl_resource *resource)
+{
+}
+
+static void
black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height);

static struct weston_surface *
@@ -2286,7 +2292,8 @@ static const struct wl_shell_surface_interface shell_surface_implementation = {
shell_surface_set_popup,
shell_surface_set_maximized,
shell_surface_set_title,
- shell_surface_set_class
+ shell_surface_set_class,
+ shell_surface_set_minimized
};

static void
--
1.7.10.4
Scott Moreau
2013-03-08 04:47:16 UTC
Permalink
---
clients/window.c | 25 ++++++++++++++++++++++++-
clients/window.h | 3 +++
src/shell.c | 9 ++++++++-
3 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/clients/window.c b/clients/window.c
index d13a1ac..7093a38 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -220,6 +220,7 @@ struct window {
int resize_needed;
int type;
int focus_count;
+ int minimized;

int resizing;
int fullscreen_method;
@@ -1925,7 +1926,7 @@ frame_button_button_handler(struct widget *widget,
display_exit(window->display);
break;
case FRAME_BUTTON_MINIMIZE:
- fprintf(stderr,"Minimize stub\n");
+ window_set_minimized(window, !window->minimized);
break;
case FRAME_BUTTON_MAXIMIZE:
window_set_maximized(window, window->type != TYPE_MAXIMIZED);
@@ -3326,11 +3327,17 @@ handle_unmaximize(void *data, struct wl_shell_surface *shell_surface)
static void
handle_minimize(void *data, struct wl_shell_surface *shell_surface)
{
+ struct window *window = data;
+
+ window->minimized = 1;
}

static void
handle_unminimize(void *data, struct wl_shell_surface *shell_surface)
{
+ struct window *window = data;
+
+ window->minimized = 0;
}

static const struct wl_shell_surface_listener shell_surface_listener = {
@@ -3472,6 +3479,22 @@ window_set_maximized(struct window *window, int maximized)
}

void
+window_set_minimized(struct window *window, int minimized)
+{
+ if (!window->display->shell)
+ return;
+
+ if (window->minimized == minimized)
+ return;
+
+ if (minimized) {
+ wl_shell_surface_set_minimized(window->shell_surface);
+ window->minimized = 1;
+ } else
+ window->minimized = 0;
+}
+
+void
window_set_user_data(struct window *window, void *data)
{
window->user_data = data;
diff --git a/clients/window.h b/clients/window.h
index c2946d8..331ce23 100644
--- a/clients/window.h
+++ b/clients/window.h
@@ -313,6 +313,9 @@ void
window_set_maximized(struct window *window, int maximized);

void
+window_set_minimized(struct window *window, int maximized);
+
+void
window_set_user_data(struct window *window, void *data);

void *
diff --git a/src/shell.c b/src/shell.c
index f3877d2..fd1411b 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1470,6 +1470,9 @@ shell_surface_minimize(struct shell_surface *shsurf)
shsurf->saved_type = shsurf->type;
shsurf->minimized = 1;

+ send_surface_data_focused_state(surface);
+ wl_shell_surface_send_minimize(&shsurf->resource);
+
/* Focus next surface in stack */
wl_list_for_each(seat, &compositor->seat_list, link)
if (seat->seat.keyboard &&
@@ -1486,7 +1489,6 @@ shell_surface_minimize(struct shell_surface *shsurf)
wl_keyboard_set_focus(seat->seat.keyboard, NULL);
}

- send_surface_data_focused_state(surface);
weston_compositor_damage_all(compositor);
}

@@ -1506,6 +1508,7 @@ surface_unminimize(struct shell_surface *shsurf, struct workspace *ws)
shell_surface_focus(shsurf);
send_surface_data_focused_state(surface);
shsurf->minimized = false;
+ wl_shell_surface_send_unminimize(&shsurf->resource);
weston_compositor_damage_all(compositor);
}

@@ -1947,6 +1950,10 @@ static void
shell_surface_set_minimized(struct wl_client *client,
struct wl_resource *resource)
{
+ struct shell_surface *shsurf = resource->data;
+
+ shell_surface_minimize(shsurf);
+ send_surface_data_minimized_state(shsurf->surface);
}

static void
--
1.7.10.4
Scott Moreau
2013-03-08 04:47:17 UTC
Permalink
---
protocol/desktop-shell.xml | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/protocol/desktop-shell.xml b/protocol/desktop-shell.xml
index 51219d2..20e2eb9 100644
--- a/protocol/desktop-shell.xml
+++ b/protocol/desktop-shell.xml
@@ -87,6 +87,12 @@
The shell can use this interface to receive surface information or make
requests for this surface.
</description>
+ <request name="maximize">
+ <description summary="ask the compositor to maximize the surface"/>
+ </request>
+ <request name="unmaximize">
+ <description summary="ask the compositor to unmaximize the surface"/>
+ </request>
<request name="minimize">
<description summary="ask the compositor to minimize the surface"/>
</request>
@@ -113,6 +119,10 @@
<description summary="send the surface object title to the shell"/>
<arg name="title" type="string"/>
</event>
+ <event name="maximized">
+ <description summary="send the surface object maximize state to the shell"/>
+ <arg name="maximized" type="int"/>
+ </event>
<event name="minimized">
<description summary="send the surface object minimize state to the shell"/>
<arg name="minimized" type="int"/>
--
1.7.10.4
Scott Moreau
2013-03-08 04:47:18 UTC
Permalink
Add maximize button for list item drop down menu.
---
clients/desktop-shell.c | 44 +++++++++++++++++++++++++++++++++++++++-----
clients/window.c | 6 ++++++
src/shell.c | 23 +++++++++++++++++++++++
3 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
index 1fa7387..4b2f805 100644
--- a/clients/desktop-shell.c
+++ b/clients/desktop-shell.c
@@ -70,7 +70,7 @@ struct surface {
struct desktop *desktop;
uint32_t output_mask;
char *title;
- int minimized, focused;
+ int maximized, minimized, focused;

/* One window list item per panel of the surface's output_mask */
struct wl_list item_list;
@@ -1159,7 +1159,7 @@ static int
panel_list_item_enter_handler(struct widget *widget, struct input *input,
float x, float y, void *data)
{
- struct list_item *item = data;
+ struct list_item *item = data, *t_item;

item->x = x;
item->y = y;
@@ -1167,6 +1167,13 @@ panel_list_item_enter_handler(struct widget *widget, struct input *input,
item->focused = 1;
widget_schedule_redraw(widget);

+ wl_list_for_each(t_item, &item->panel->window_list, link) {
+ if(item == t_item)
+ continue;
+ t_item->highlight = 0;
+ t_item->focused = 0;
+ }
+
return CURSOR_LEFT_PTR;
}

@@ -1201,7 +1208,17 @@ list_item_menu_handle_button(struct list_item *item, int index)
surface->minimized = 1;
}
break;
- case 1: /* Close */
+ case 1: /* (Un)Maximize */
+ if (surface->maximized) {
+ surface_data_unmaximize(surface->surface_data);
+ surface->maximized = 0;
+ }
+ else {
+ surface_data_maximize(surface->surface_data);
+ surface->maximized = 1;
+ }
+ break;
+ case 2: /* Close */
surface_data_close(surface->surface_data);
break;
default:
@@ -1231,7 +1248,7 @@ list_item_menu_func(struct window *window, int index, void *data)
}
}

-#define MENU_ENTRIES 2
+#define MENU_ENTRIES 3

static void
list_item_show_menu(struct list_item *item, struct input *input, uint32_t time)
@@ -1241,7 +1258,8 @@ list_item_show_menu(struct list_item *item, struct input *input, uint32_t time)
static const char *entries[MENU_ENTRIES];

entries[0] = item->surface->minimized ? "Unminimize" : "Minimize";
- entries[1] = "Close";
+ entries[1] = item->surface->maximized ? "Unmaximize" : "Maximize";
+ entries[2] = "Close";

panel = item->panel;
input_get_position(input, &x, &y);
@@ -1503,6 +1521,21 @@ surface_data_set_title(void *data,
}

static void
+surface_data_set_maximized_state(void *data,
+ struct surface_data *surface_data,
+ int maximized)
+{
+ struct desktop *desktop;
+ struct surface *surface = data;
+
+ desktop = surface->desktop;
+
+ surface->maximized = maximized;
+
+ desktop_update_list_items(desktop, surface);
+}
+
+static void
surface_data_set_minimized_state(void *data,
struct surface_data *surface_data,
int minimized)
@@ -1568,6 +1601,7 @@ surface_data_destroy_handler(void *data, struct surface_data *surface_data)
static const struct surface_data_listener surface_data_listener = {
surface_data_set_output_mask,
surface_data_set_title,
+ surface_data_set_maximized_state,
surface_data_set_minimized_state,
surface_data_set_focused_state,
surface_data_destroy_handler
diff --git a/clients/window.c b/clients/window.c
index 7093a38..d5a1898 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -3317,11 +3317,17 @@ handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
static void
handle_maximize(void *data, struct wl_shell_surface *shell_surface)
{
+ struct window *window = data;
+
+ window_set_maximized(window, 1);
}

static void
handle_unmaximize(void *data, struct wl_shell_surface *shell_surface)
{
+ struct window *window = data;
+
+ window_set_maximized(window, 0);
}

static void
diff --git a/src/shell.c b/src/shell.c
index fd1411b..1e2eb52 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1508,6 +1508,7 @@ surface_unminimize(struct shell_surface *shsurf, struct workspace *ws)
shell_surface_focus(shsurf);
send_surface_data_focused_state(surface);
shsurf->minimized = false;
+ shsurf->type = shsurf->saved_type;
wl_shell_surface_send_unminimize(&shsurf->resource);
weston_compositor_damage_all(compositor);
}
@@ -1526,6 +1527,24 @@ shell_surface_unminimize(struct shell_surface *shsurf)
}

static void
+surface_data_maximize_handler(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct shell_surface *shsurf = resource->data;
+
+ wl_shell_surface_send_maximize(&shsurf->resource);
+}
+
+static void
+surface_data_unmaximize_handler(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct shell_surface *shsurf = resource->data;
+
+ wl_shell_surface_send_unmaximize(&shsurf->resource);
+}
+
+static void
surface_data_minimize_handler(struct wl_client *client,
struct wl_resource *resource)
{
@@ -1593,6 +1612,8 @@ surface_data_destroy_handler(struct wl_client *client,

static const struct surface_data_interface
surface_data_implementation = {
+ surface_data_maximize_handler,
+ surface_data_unmaximize_handler,
surface_data_minimize_handler,
surface_data_unminimize_handler,
surface_data_focus_handler,
@@ -1791,6 +1812,7 @@ reset_shell_surface_type(struct shell_surface *surface)
weston_surface_set_position(surface->surface,
surface->saved_x,
surface->saved_y);
+ surface_data_send_maximized(surface->surface_data, 0);
break;
case SHELL_SURFACE_NONE:
case SHELL_SURFACE_TOPLEVEL:
@@ -1827,6 +1849,7 @@ set_surface_type(struct shell_surface *shsurf)
shsurf->saved_x = surface->geometry.x;
shsurf->saved_y = surface->geometry.y;
shsurf->saved_position_valid = true;
+ surface_data_send_maximized(shsurf->surface_data, 1);
break;

case SHELL_SURFACE_FULLSCREEN:
--
1.7.10.4
Scott Moreau
2013-03-16 16:39:42 UTC
Permalink
This entire series committed to gh next.
Post by Scott Moreau
This series implements a window list for the panel. It introduces protocol for
minimize/maximize control of shell_surfaces. It also provides a basic
minimize implementation.
Admittedly I am a little confused about the interface version system. I am
wondering if we should have something such as a changlog file kept in the
protocol folder.
Aside from this, the window list seems to work pretty well with all the new
functionality that it offers. Minimized buttons are hooked up by this series
as well, which means we can start fleshing out the decorations in other clients
and toolkits. The list operation is dynamic, supporting multiple outputs.
- Rebased to latest master
http://lists.freedesktop.org/archives/wayland-devel/2012-November/006485.html
- Additional bug fixes
http://youtu.be/nv5isdcxBcY
Continue reading on narkive:
Loading...