TTMEDIA_PTYPE_DECLARE(3)

πŸ“¦
version 2.5.3 Β· 2 versions
, in nixos 26.05

✨ NAME ✨

okay so this is ttmedia_ptype_declare, a library call, and it's giving declare the ptype of a Media Exchange media editor πŸ’…. Bestie, the whole vibe is: it initializes an editor that implements the Media Exchange message interface for one particular media type.


πŸ› οΈ SYNOPSIS πŸ› οΈ

       #include &<Tt/tttk.h>
       Tt_status ttmedia_ptype_declare(
       const char *ptype,
       int base_opnum,
       Ttmedia_load_pat_cb cb,
       void *clientdata,
       int declare);

πŸ’– DESCRIPTION πŸ’–

The ttmedia_ptype_declare function is used to initialize an editor that implements the Media Exchange message interface for a particular media type. It notifies the ToolTalk service that the cb callback is to be called when the editor is asked to edit a document of the kind supported by ptype. ✨

It also installs an implementation-specific opnum callback on a series of signatures that ptype is assumed to contain. These signatures are listed below, with their corresponding opnum offsets. Opnums in ptype for these signatures start at base_opnum, which must be zero or a multiple of 1000. The implementation-specific opnum callback will pass clientdata to cb when a request is received that matches one of these signatures.

If declare is True, ttmedia_ptype_declare calls tt_ptype_declare with the ptype argument. If ptype implements Media Exchange for several different media types, then ttmedia_ptype_declare can be called multiple times, with a different base_opnum each time, and with declare being True only once. 🫢

The Ttmedia_load_pat_cb argument is a callback defined as:

       Tt_message (*Ttmedia_load_pat_cb)(Tt_message msg,
               void *clientdata,
               Tttk_op op,
               Tt_status diagnosis,
               unsigned char *contents,
               int len,
               char *file,
               char *docname);

The msg argument is a TT_REQUEST in Tt_state TT_SENT. The client program becomes responsible for either failing, rejecting or replying to it. This can either be done inside the callback, or the message can be saved and dismissed later (that is, after the callback returns). Usually, the callback will either immediately reject/fail the request, or it will start processing the request, perhaps by associating it with a new window. When the request is finally dismissed, it should be destroyed, for example, using tt_message_destroy. πŸŽ€

If the callback knows it will handle the request (either fail or reply to it, but not reject it), then it should call ttdt_message_accept(3). But if the return value of tt_message_status(3) of msg is TT_WRN_START_MESSAGE, then the callback should probably do ttdt_session_join, and perhaps a ttdt_file_join, before accepting the message. The op argument is the op of the incoming request, one of TTME_COMPOSE, TTME_EDIT or TTME_DISPLAY. 🌸

The diagnosis argument is the recommended error code; if the ToolTalk service detects a problem with the request (for example, TT_DESKTOP_ENODATA), then it passes in the error code that it recommends the request should be failed with. If diagnosis was not TT_OK and the Ttmedia_load_pat_cb returns msg, then the ToolTalk service will fail and destroy msg.

🚨 okay so here's the dramatic part, and it matters: the ToolTalk service does not simply fail the request transparently, because the request may be the reason that the client process was started by ToolTalk in the first place. So if diagnosis is not TT_OK and the tt_message_status of msg is TT_WRN_START_MESSAGE, then many applications will decide that they have no reason to continue running. If such an application chooses to exit in the callback, then it should first dismiss the request. Otherwise, it can set some global flag, return msg (thus allowing the ToolTalk service to dismiss the message), and then have main check the flag and exit before even entering the event loop. (Exiting without dismissing the request would fail it with status TT_ERR_PROCID, instead of with diagnostic.) πŸ’€

The contents, len, and file arguments represent the contents of the arriving document. If len is zero, then the document is contained in file. If contents or file are non-NULL, they can be freed using tt_free.

The docname argument is the name of the document, if any. The clientdata argument is the clientdata passed to ttmedia_ptype_declare. πŸ¦‹

A Ttmedia_load_pat_cb should return zero if it processes msg successfully, or a tt_error_pointer cast to Tt_message if processing results in an error. It should return the msg if it does not consume it. If diagnosis is not TT_OK and msg is returned, then the ToolTalk service will consume (namely, fail and destroy) it. If diagnosis is TT_OK and msg is returned, then the ToolTalk service will pass TT_CALLBACK_CONTINUE down the call stack, so that msg will be offered to other callbacks or (more likely) be returned from tt_message_receive(3). Applications will rarely want msg to get processed by other callbacks or in the main event loop. πŸ‘‘


πŸ”₯ RETURN VALUE πŸ”₯

Upon successful completion, the ttmedia_ptype_declare function returns the status of the operation. The application can use tt_ptr_error(3) to extract one of the following Tt_status values from the returned handle:

  • TT_OK β€” The operation completed successfully. πŸŽ‰
  • TT_ERR_NOMP β€” The ttsession(1) process is not running and the ToolTalk service cannot restart it. 😭
  • TT_ERR_POINTER β€” The pointer passed does not point to an object of the correct type for this operation. πŸ™ˆ
  • TT_ERR_PROCID β€” The specified process identifier is out of date or invalid. ⭐
  • TT_ERR_PTYPE β€” The specified process type is not the name of an installed process type. πŸ’œ

✨ EXAMPLES ✨

okay bestie, this is the part you actually came for πŸ’… β€” here's the typical algorithm of a Ttmedia_load_pat_cb, like, the shape you'll see in the wild. Real one. Not a drill 🫢

Tt_message
myAcmeSheetLoadCB(
        Tt_message      msg,
        void           *client_data,
        Tttk_op         op,
        Tt_status       diagnosis,
        unsigned char  *contents,
        int             len,
        char           *file,
        char           *docname
)
{
        Tt_status status = TT_OK;
        if (diagnosis != TT_OK) {
                /* toolkit detected an error */
                if (tt_message_status(msg) == TT_WRN_START_MESSAGE) {
                        /*
                         * Error is in start message!  We now have no
                         * reason to live, so tell main() to exit().
                         */
                        myAbortCode = 2;
                }
                /* let toolkit handle the error */
                return msg;
        }
        if ((op == TTME_COMPOSE)&&&&(file == 0)) {
                /* open empty new buffer */
        } else if (len > 0) {
                /* load contents into new buffer */
        } else if (file != 0) {
                if (ttdt_Get_Modified(msg, file, TT_BOTH, myCntxt, 5000)) {
                        switch(myUserChoice("Save, Revert, Ignore?")) {
                            case 0:
                                ttdt_Save(msg, file, TT_BOTH, myCntxt, 5000);
                                break;
                            case 1:
                                ttdt_Revert(msg, file, TT_BOTH, myCntxt, 5000);
                                break;
                        }
                }
                /* load file into new buffer */
        } else {
                tttk_message_fail(msg, TT_DESKTOP_ENODATA, 0, 1);
                tt_free(contents); tt_free(file); tt_free(docname);
                return 0;
        }
        int w, h, x, y = INT_MAX;
        ttdt_sender_imprint_on(0, msg, 0, &&w, &&h, &&x, &&y, myCntxt, 5000);
        positionMyWindowRelativeTo(w, h, x, y);
        if (maxBuffersAreNowOpen) {
                /* Un-volunteer to handle future requests until less busy */
                tt_ptype_undeclare("Acme_Calc");
        }
        if (tt_message_status(msg) == TT_WRN_START_MESSAGE) {
                /*
                 * Join session before accepting start message,
                 * to prevent unnecessary starts of our ptype
                 */
                ttdt_session_join(0, myContractCB, myShell, 0, 1);
        }
        ttdt_message_accept(msg, myContractCB, myShell, 0, 1, 1);
        tt_free(contents); tt_free(file); tt_free(docname);
        return 0;
}

let me walk you through it because the vibes are immaculate once you clock the flow 🌸

first up, Tt_status status = TT_OK; and then the vibe check: if diagnosis != TT_OK, the toolkit already sniffed out an error, so no cap, we do not try to fix it ourselves. and if tt_message_status(msg) == TT_WRN_START_MESSAGE, the error is in the start message β€” the code's own comment says "We now have no reason to live, so tell main() to exit()" 😭 so we set myAbortCode = 2; and then return msg; so the toolkit can handle the error itself. we are ✨delegating✨.

then the big branching moment, and honestly it's giving choose-your-own-adventure πŸ¦‹

  • if (op == TTME_COMPOSE)&&&&(file == 0) β†’ open an empty new buffer, fresh out the wrapper
  • else if len > 0 β†’ load contents into a new buffer
  • else if file != 0 β†’ this is the spicy one πŸ”₯ we call ttdt_Get_Modified(msg, file, TT_BOTH, myCntxt, 5000) and if it comes back truthy we hit myUserChoice("Save, Revert, Ignore?") and switch on the answer: case 0: fires ttdt_Save(msg, file, TT_BOTH, myCntxt, 5000); and break;, case 1: fires ttdt_Revert(msg, file, TT_BOTH, myCntxt, 5000); and break;. then, after all that, load the file into a new buffer.
  • else β†’ the saddest branch πŸ˜” tttk_message_fail(msg, TT_DESKTOP_ENODATA, 0, 1);, then tt_free(contents); tt_free(file); tt_free(docname); (yes all three, don't leave a leak bestie, that's not cute) and return 0;

after the branching we get the window placement glow-up: int w, h, x, y = INT_MAX; and then ttdt_sender_imprint_on(0, msg, 0, &&w, &&h, &&x, &&y, myCntxt, 5000); so the sender can stamp their info onto us, and then positionMyWindowRelativeTo(w, h, x, y); β€” the window knows where to be πŸ’–

then the self-care check: if (maxBuffersAreNowOpen) we "Un-volunteer to handle future requests until less busy" by calling tt_ptype_undeclare("Acme_Calc"); β€” like, she's booked and busy, respectfully declining new work πŸŽ€

and if tt_message_status(msg) == TT_WRN_START_MESSAGE, we ttdt_session_join(0, myContractCB, myShell, 0, 1); β€” the comment spells out why: "Join session before accepting start message, to prevent unnecessary starts of our ptype". then it's the main character moment: ttdt_message_accept(msg, myContractCB, myShell, 0, 1, 1);

finally, cleanup queen behaviour: tt_free(contents); tt_free(file); tt_free(docname); and return 0; 🧚 periodt.


πŸŽ€ the ptype signature layout πŸŽ€

and here's the signature layout to which ptype should conform β€” this is the blueprint, iykyk πŸ‘‘

ptype Acme_Calc {
    start "acalc";
    handle:
        /*
         * Instantiate Acme_Sheet
         * Include in tool's ptype if tool can be a document factory.
         */
        session Instantiate(in Acme_Sheet  template) => start opnum = 401;
        /*
         * Display Acme_Sheet
         * Include in tool's ptype if tool can display a document.
         */
        session Display( in    Acme_Sheet  contents) => start opnum = 1;
        session Display( in    Acme_Sheet  contents,
                         in    messageID   counterfoil) => start opnum = 2;
        session Display( in    Acme_Sheet  contents,
                         in    title       docName) => start opnum = 3;
        session Display( in    Acme_Sheet  contents,
                         in    messageID   counterfoil,
                         in    title       docName) => start opnum = 4;
        /*
         * Edit Acme_Sheet
         * Include in tool's ptype if tool can edit a document.
         */
        session Edit(    inout Acme_Sheet  contents) => start opnum = 101;
        session Edit(    inout Acme_Sheet  contents,
                         in    messageID   counterfoil) => start opnum = 102;
        session Edit(    inout Acme_Sheet  contents,
                         in    title       docName) => start opnum = 103;
        session Edit(    inout Acme_Sheet  contents,
                         in    messageID   counterfoil,
                         in    title       docName) => start opnum = 104;
        /*
         * Compose Acme_Sheet
         * Include in tool's ptype if tool can compose a document from scratch.
         */
        session Edit(    out   Acme_Sheet  contents) => start opnum = 201;
        session Edit(    out   Acme_Sheet  contents,
                         in    messageID   counterfoil) => start opnum = 202;
        session Edit(    out   Acme_Sheet  contents,
                         in    title       docName) => start opnum = 203;
        session Edit(    out   Acme_Sheet  contents,
                         in    messageID   counterfoil,
                         in    title       docName) => start opnum = 204;
        /*
         * Mail Acme_Sheet
         * Include in tool's ptype if tool can mail a document.
         */
        session Mail(    in    Acme_Sheet  contents) => start opnum = 301;
        session Mail(    inout Acme_Sheet  contents) => start opnum = 311;
        session Mail(    inout Acme_Sheet  contents,
                         in    title       docName) => start opnum = 313;
        session Mail(    out   Acme_Sheet  contents) => start opnum = 321;
        session Mail(    out   Acme_Sheet  contents,
                         in    messageID   counterfoil) => start opnum = 323;
};

like, look at her 😭 the start "acalc"; line, the handle: block, the opnum values all neatly fanned out β€” she's organised, she's iconic, she's the moment 🌈


πŸ’œ SEE ALSO πŸ’œ


and that's the whole page, bestie πŸ’– this one ships under the ttmedia_ptype_declare(library call) footer β€” that's your source/version stamp and date situation right there, go click the links, mwah πŸ’‹πŸ‘‘

πŸ“¦ where this page comes from