int vrpn_BaseClassUnique::send_text_message(const char *msg,
                                            struct timeval timestamp,
                                            vrpn_TEXT_SEVERITY type,
                                            vrpn_uint32 level)
{
    char buffer[2 * sizeof(vrpn_int32) + vrpn_MAX_TEXT_LEN];
    size_t len = strlen(msg) + 1; // +1 is for the NULL terminator

    if (len > vrpn_MAX_TEXT_LEN) {
        fprintf(stderr, "vrpn_BaseClassUnique::send_message: Attempt to encode "
                        "string that is too long\n");
        return -1;
    }

    // send type, level and message

    encode_text_message_to_buffer(buffer, type, level, msg);
    if (d_connection) {
        d_connection->pack_message(sizeof(buffer), timestamp, d_text_message_id,
                                   d_sender_id, buffer,
                                   vrpn_CONNECTION_RELIABLE);
    }

    return 0;
}
int vrpn_Imager_Stream_Buffer::handle_server_messages(
    const vrpn_HANDLERPARAM &p)
{
    // Handle begin_frame message very specially, because it has all sorts
    // of interactions with throttling and missed-frame reporting.
    if (p.type == d_server_begin_frame_m_id) {
        // This duplicates code from the send_begin_frame() method in
        // the vrpn_Imager_Server base class that handles throttling.
        // It further adds code to handle throttling when the queue to
        // the initial thread is too full.

        // If we are throttling frames and the frame count has gone to zero,
        // then increment the number of frames missed and do not add this
        // message to the queue.
        if (d_server_frames_to_send == 0) {
            d_server_dropped_due_to_throttle++;
            return 0;
        }

        // If there are too many frames in the queue already,
        // add one to the number lost due to throttling (which
        // will prevent region and end-of-frame messages until the next
        // begin_frame message) and break without forwarding the message.
        if (d_shared_state.get_frames_in_queue() >= 2) {
            d_server_dropped_due_to_throttle++;
            return 0;
        }

        // If we missed some frames due to throttling, but are now
        // sending frames again, report how many we lost due to
        // throttling.  This is incremented both for client-requested
        // throttling and to queue-overflow throttling.
        if (d_server_dropped_due_to_throttle > 0) {
            // We create a new message header and body, using the server's
            // type IDs, and then transcode and send the message through
            // the initial connection.
            vrpn_HANDLERPARAM tp = p;
            vrpn_float64
                fbuf[vrpn_CONNECTION_TCP_BUFLEN / sizeof(vrpn_float64)];
            char *msgbuf = (char *)fbuf;
            int buflen = sizeof(fbuf);
            tp.type = d_server_discarded_frames_m_id;

            if (vrpn_buffer(&msgbuf, &buflen,
                            d_server_dropped_due_to_throttle)) {
                fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_"
                                "messages: Can't pack count\n");
                return -1;
            }
            tp.buffer = static_cast<char *>(static_cast<void *>(fbuf));
            tp.payload_len = sizeof(fbuf) - buflen;

            if (!transcode_and_send(tp)) {
                fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_"
                                "messages: Can't send discarded frames "
                                "count\n");
                return -1;
            }

            d_server_dropped_due_to_throttle = 0;
        }

        // If we are throttling, then decrement the number of frames
        // left to send.
        if (d_server_frames_to_send > 0) {
            d_server_frames_to_send--;
        }

        // No throttling going on, so add the message to the outgoing queue and
        // also increment the count of how many outstanding frames are in the
        // queue.
        if (!transcode_and_send(p)) {
            fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
                            " Can't transcode and send\n");
            return -1;
        }
        d_shared_state.increment_frames_in_queue();

        // Handle the end_frame and all of the region messages in a similar
        // manner,
        // dropping them if throttling is going on and passing them on if not.
        // This duplicates code from the send_end_frame() and the region
        // send methods in the vrpn_Imager_Server base class that handles
        // throttling.
    }
    else if ((p.type == d_server_end_frame_m_id) ||
             (p.type == d_server_regionu8_m_id) ||
             (p.type == d_server_regionu12in16_m_id) ||
             (p.type == d_server_regionu16_m_id) ||
             (p.type == d_server_regionf32_m_id)) {

        // If we are discarding frames, do not queue this message.
        if (d_server_dropped_due_to_throttle > 0) {
            return 0;
        }

        // No throttling going on, so add this message to the outgoing queue.
        if (!transcode_and_send(p)) {
            fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
                            " Can't transcode and send\n");
            return -1;
        }

        // Send these messages on without modification
        // (Currently, these are description messages and discarded-frame
        // messages.  It also includes the generic pong response and any
        // text messages.)
    }
    else if ((p.type == d_server_description_m_id) ||
             (p.type == d_server_discarded_frames_m_id) ||
             (p.type == d_server_text_m_id) || (p.type == d_server_pong_m_id)) {
        if (!transcode_and_send(p)) {
            fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
                            " Can't transcode and send\n");
            return -1;
        }

        // Ignore these messages without passing them on.
    }
    else if ((p.type == d_server_ping_m_id)) {
        return 0;

        // We need to throw a warning here on unexpected types so that we get
        // some
        // warning if additional messages are added.  This code is fragile
        // because it
        // relies on us knowing the types of base-level and imager messages and
        // catching
        // them all.  This way, at least we'll know if we miss one.
    }
    else {
        // We create a new message header and body, using the server's
        // type IDs, and then transcode and send the message through
        // the initial connection.  This is a text message saying that we
        // got a message of unknown type.
        vrpn_HANDLERPARAM tp = p;
        char buffer[2 * sizeof(vrpn_int32) + vrpn_MAX_TEXT_LEN];
        char msg[vrpn_MAX_TEXT_LEN];
        tp.type = d_server_text_m_id;
        tp.buffer = buffer;
        tp.payload_len = sizeof(buffer);
        sprintf(msg, "Unknown message type from server: %d",
                static_cast<int>(p.type));
        encode_text_message_to_buffer(buffer, vrpn_TEXT_ERROR, 0, msg);
        if (!transcode_and_send(tp)) {
            fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
                            " Can't transcode text message\n");
            return -1;
        }
    }

    return 0;
}