-
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
557a0d3
commit 8a3ddc0
Showing
32 changed files
with
1,414 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,12 @@ | ||
Louvre (2.7.0-1) | ||
|
||
# New Protocols | ||
|
||
* XDG Activation: See LActivationTokenManager and LActivationToken for details. | ||
|
||
-- Eduardo Hopperdietzel <[email protected]> Thu, 22 Aug 2024 20:00:51 -0400 | ||
|
||
|
||
Louvre (2.6.0-1) | ||
|
||
# New Protocols | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
2.6.0 | ||
2.7.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#include <LActivationTokenManager.h> | ||
#include <LClient.h> | ||
#include <LSurface.h> | ||
|
||
using namespace Louvre; | ||
|
||
const std::unordered_map<std::string, LActivationToken>::iterator LActivationToken::destroy() const noexcept | ||
{ | ||
auto &tokens { Louvre::activationTokenManager()->m_tokens }; | ||
return tokens.erase(tokens.find(*m_key)); | ||
} | ||
|
||
LActivationToken::LActivationToken(LClient *creator, LSurface *origin, LEvent *triggeringEvent, std::string &&toActivateAppId) noexcept : | ||
m_creator(creator), | ||
m_origin(origin), | ||
m_triggeringEvent(triggeringEvent), | ||
m_toActivateAppId(toActivateAppId) | ||
{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
#ifndef LACTIVATIONTOKEN_H | ||
#define LACTIVATIONTOKEN_H | ||
|
||
#include <LObject.h> | ||
#include <LWeak.h> | ||
#include <LEvent.h> | ||
#include <memory> | ||
#include <chrono> | ||
|
||
/** | ||
* @brief Token for activating surfaces | ||
* | ||
* This class represents a token used in LActivationTokenManager::activateSurfaceRequest(). | ||
* | ||
* For more details, refer to the LActivationTokenManager class documentation. | ||
*/ | ||
class Louvre::LActivationToken final : public LObject | ||
{ | ||
public: | ||
|
||
/** | ||
* @brief The client that requested the token creation. | ||
* | ||
* @note This may, but does not necessarily, refer to the same client that requests surface activation. | ||
* | ||
* @return A valid pointer during LActivationTokenManager::createTokenRequest(), but it may return `nullptr` | ||
* if accessed later, for example, if the client has disconnected. | ||
*/ | ||
LClient *creator() const noexcept | ||
{ | ||
return m_creator.get(); | ||
} | ||
|
||
/** | ||
* @brief The surface of the client that created the token. | ||
* | ||
* This refers to the surface where the triggeringEvent() was sent. | ||
* | ||
* @note This is different from the surface intended to be activated. | ||
* | ||
* @warning Clients are not obligated to provide this hint, so this method may return `nullptr`. | ||
*/ | ||
LSurface *origin() const noexcept | ||
{ | ||
return m_origin.get(); | ||
} | ||
|
||
/** | ||
* @brief Triggering event | ||
* | ||
* Clients typically request token creation in response to an input event. The default implementation of | ||
* LActivationTokenManager::createTokenRequest() only accepts requests containing a valid triggeringEvent(). | ||
* | ||
* @see origin() | ||
* | ||
* @warning This value is optional, if the client does not specify a triggering event, this method returns `nullptr`. | ||
*/ | ||
const LEvent *triggeringEvent() const noexcept | ||
{ | ||
return m_triggeringEvent.get(); | ||
} | ||
|
||
/** | ||
* @brief Number of times the token has been used to activate a surface. | ||
* | ||
* Incremented by 1 before each LActivationTokenManager::activateSurfaceRequest(). | ||
*/ | ||
UInt32 timesUsed() const noexcept | ||
{ | ||
return m_timesUsed; | ||
} | ||
|
||
/** | ||
* @brief Time the token was created. | ||
* | ||
* @see LActivationTokenManager::destroyTokensOlderThanMs(). | ||
* | ||
* @return The time point when the token was created. | ||
*/ | ||
const std::chrono::time_point<std::chrono::steady_clock> &created() const noexcept | ||
{ | ||
return m_created; | ||
} | ||
|
||
/** | ||
* @brief The app ID of the client to be activated. | ||
* | ||
* This value is optional, if the token creator does not assign an app ID, this method returns an empty string. | ||
*/ | ||
const std::string &toActivateAppId() const noexcept | ||
{ | ||
return m_toActivateAppId; | ||
} | ||
|
||
/** | ||
* @brief The unique and random token string generated by Louvre. | ||
* | ||
* @return The token string. | ||
*/ | ||
const std::string &token() const noexcept | ||
{ | ||
return *m_key; | ||
} | ||
|
||
/** | ||
* @brief Invalidates and destroys the token. | ||
* | ||
* Removes it from LActivationTokenManager::tokens(). | ||
* | ||
* Once destroyed, no client will be able to use it to activate surfaces. | ||
* | ||
* @return An iterator to the next element in LActivationTokenManager::tokens(). | ||
*/ | ||
const std::unordered_map<std::string, LActivationToken>::iterator destroy() const noexcept; | ||
|
||
LActivationToken(const LActivationToken&) = delete; | ||
LActivationToken &operator=(const LActivationToken&) = delete; | ||
LActivationToken(LActivationToken&&) = default; | ||
|
||
private: | ||
friend class Protocols::XdgActivation::RXdgActivationToken; | ||
|
||
LActivationToken( | ||
LClient *creator, | ||
LSurface *origin, | ||
LEvent *triggeringEvent, | ||
std::string &&toActivateAppId) noexcept; | ||
|
||
LWeak<LClient> m_creator; | ||
LWeak<LSurface> m_origin; | ||
std::unique_ptr<LEvent> m_triggeringEvent; | ||
std::string m_toActivateAppId; | ||
std::chrono::time_point<std::chrono::steady_clock> m_created { | ||
std::chrono::steady_clock::now() | ||
}; | ||
UInt32 m_timesUsed { 0 }; | ||
const std::string *m_key { nullptr }; | ||
}; | ||
|
||
#endif // LACTIVATIONTOKEN_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#include <LActivationTokenManager.h> | ||
#include <cassert> | ||
|
||
using namespace Louvre; | ||
|
||
LActivationTokenManager::LActivationTokenManager(const void *params) noexcept : LFactoryObject(FactoryObjectType) | ||
{ | ||
assert(params != nullptr && "Invalid parameter passed to LActivationTokenManager constructor."); | ||
LActivationTokenManager**ptr { (LActivationTokenManager**) params }; | ||
assert(*ptr == nullptr && *ptr == Louvre::activationTokenManager() && "Only a single LActivationTokenManager instance can exist."); | ||
*ptr = this; | ||
} | ||
|
||
void LActivationTokenManager::destroyTokensOlderThanMs(UInt32 ms) | ||
{ | ||
if (ms == 0) | ||
{ | ||
m_tokens.clear(); | ||
return; | ||
} | ||
|
||
for (auto it = m_tokens.begin(); it != m_tokens.end();) | ||
{ | ||
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - (*it).second.created()).count() > ms) | ||
it = m_tokens.erase(it); | ||
else | ||
it++; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
#ifndef LACTIVATIONTOKENMANAGER_H | ||
#define LACTIVATIONTOKENMANAGER_H | ||
|
||
#include <LFactoryObject.h> | ||
#include <LActivationToken.h> | ||
|
||
/** | ||
* @brief Activation Token Manager | ||
* | ||
* @anchor lactivationtokenmanager_detailed | ||
* | ||
* The [XDG Activation](https://wayland.app/protocols/xdg-activation-v1) protocol allows clients to pass focus to another client's surface. | ||
* | ||
* The protocol functions as follows: | ||
* | ||
* 1. The "activator" client requests a new token from the compositor via the createTokenRequest() method. | ||
* During the request, the client can provide additional (optional) information, such as the application ID of the "target" client | ||
* (LActivationToken::toActivateAppId()), the event that triggered the request (LActivationToken::triggeringEvent()), | ||
* and the surface that received the triggering event (LActivationToken::origin()). | ||
* | ||
* 2. If the request is accepted, the compositor generates an activation token (a unique and random string) | ||
* and sends it to the client. The token is stored in the tokens() map. | ||
* | ||
* 3. The "activator" client then passes the token to the "target" client via its own communication channel (e.g., D-Bus, the **XDG_ACTIVATION_TOKEN** environment variable). | ||
* | ||
* 4. The "target" client can use the token to activate one of its surfaces by invoking the activateSurfaceRequest() method. | ||
* | ||
* The default implementations of createTokenRequest() and activateSurfaceRequest() only accept requests that provide a valid triggering event and | ||
* destroy the token after it is used, as well as those that haven't been used in the last 10 seconds. | ||
* This is for security reasons, tokens can remain valid indefinitely if desired. | ||
* | ||
* There is a unique instance of this class, created within LCompositor::createObjectRequest(), | ||
* which can be accessed globally via Louvre::activationTokenManager(). | ||
* | ||
* To disable this protocol, remove its global from LCompositor::createGlobalsRequest() | ||
* | ||
* @see LActivationToken | ||
*/ | ||
class Louvre::LActivationTokenManager : public LFactoryObject | ||
{ | ||
public: | ||
static constexpr LFactoryObject::Type FactoryObjectType = LFactoryObject::Type::LActivationTokenManager; | ||
|
||
/** | ||
* @brief LActivationTokenManager class constructor. | ||
* | ||
* There is only one instance of this class, which can be accessed globally via activationTokenManager(). | ||
* | ||
* @param params Internal parameters provided in LCompositor::createObjectRequest(). | ||
*/ | ||
LActivationTokenManager(const void *params) noexcept; | ||
|
||
/** | ||
* @brief Destructor of the LActivationTokenManager class. | ||
* | ||
* Invoked after LCompositor::onAnticipatedObjectDestruction(). | ||
*/ | ||
~LActivationTokenManager() = default; | ||
|
||
LCLASS_NO_COPY(LActivationTokenManager) | ||
|
||
/** | ||
* @brief Retrieve available tokens. | ||
* | ||
* Provides all currently available tokens, which clients can use to activate surfaces. | ||
* | ||
* To expire tokens, use LActivationToken::destroy() or the destroyTokensOlderThanMs() method. | ||
*/ | ||
const std::unordered_map<std::string, LActivationToken> &tokens() const noexcept | ||
{ | ||
return m_tokens; | ||
} | ||
|
||
/** | ||
* @brief Retrieve the current token. | ||
* | ||
* This method provides access to the token used during createTokenRequest() or activateSurfaceRequest() requests. | ||
* If neither request is in progress, this method returns `nullptr`. | ||
* | ||
* The token is not provided as an argument to those requests because the default implementation of activateSurfaceRequest() | ||
* triggers LToplevelRole::activateRequest() if the surface has a toplevel role. This way, the token can be accessed | ||
* from LToplevelRole::activateRequest() if needed. | ||
*/ | ||
LActivationToken *token() const noexcept | ||
{ | ||
return m_token.get(); | ||
} | ||
|
||
/** | ||
* @brief Destroy tokens older than the specified number of milliseconds. | ||
* | ||
* The default implementation of activateSurfaceRequest() uses this method to destroy all tokens older than 10 seconds | ||
* to prevent unnecessary tokens from remaining active. | ||
* | ||
* @see LActivationToken::destroy() and LActivationToken::created(). | ||
*/ | ||
void destroyTokensOlderThanMs(UInt32 ms); | ||
|
||
/// @name Virtual Methods | ||
/// @{ | ||
|
||
/** | ||
* @brief Request to create an activation token. | ||
* | ||
* This request is triggered each time a client wants to create a new activation token. | ||
* The activation token data can be accessed via token(). If the token is denied, `token()->destroy()` must be called, | ||
* otherwise, the token will be retained in the tokens() map, and clients will be allowed to use it for activateSurfaceRequest(). | ||
* | ||
* The default implementation only accepts tokens generated by the currently focused client. | ||
* | ||
* #### Default implementation | ||
* @snippet LActivationTokenManagerDefault.cpp createTokenRequest | ||
*/ | ||
virtual void createTokenRequest(); | ||
|
||
/** | ||
* @brief Request to activate a surface. | ||
* | ||
* This request indicates that a client wants to activate one of its surfaces and is only triggered if the client | ||
* provides one of the tokens(), which can be accessed during the request with token(). | ||
* | ||
* The default implementation verifies if the token is recent and destroys it immediately after use, | ||
* along with any tokens older than 10 seconds. | ||
* | ||
* If the token is not destroyed, LActivationToken::timesUsed() is incremented by 1. | ||
* | ||
* @param surface The surface to activate. | ||
* | ||
* #### Default implementation | ||
* @snippet LActivationTokenManagerDefault.cpp activateSurfaceRequest | ||
*/ | ||
virtual void activateSurfaceRequest(LSurface *surface); | ||
|
||
/// @} | ||
|
||
private: | ||
friend class Protocols::XdgActivation::GXdgActivation; | ||
friend class Protocols::XdgActivation::RXdgActivationToken; | ||
friend class LActivationToken; | ||
std::unordered_map<std::string, LActivationToken> m_tokens; | ||
LWeak<LActivationToken> m_token; | ||
}; | ||
|
||
#endif // LACTIVATIONTOKENMANAGER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.